bzoj3073 [Pa2011]Journeys

本文介绍了一种处理大规模稠密图的最短路径算法,利用线段树和堆优化Dijkstra算法来解决从一个特定节点到达其他所有节点所需的最小边数问题。通过巧妙地构造两棵树来减少边的数量,提高了算法效率。

Description


Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路。N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a,b),(c,d)表示,对于任意两个国家x,y,如果a<=x<=b,c<=y<=d,那么在xy之间建造一条道路。Seter保证一条道路不会修建两次,也保证不会有一个国家与自己之间有道路。
Seter好不容易建好了所有道路,他现在在位于P号的首都。Seter想知道P号国家到任意一个国家最少需要经过几条道路。当然,Seter保证P号国家能到任意一个国家。

注意:可能有重边

N<=500000,M<=100000。
1<=A<=B<=N,1<=C<=D<=N。

Solution


显然直接连边时间和空间复杂度都不能接受,考虑更加优秀的方法

回想类比一下倍增并查集,这里建两棵线段树,一棵只从儿子连向父亲记为up树(名字随意啦,一棵只从父亲连向儿子记为down树。修改的时候查询线段树上的区间,并同时新建节点,分别从up树连向新节点、新节点连向down树,边权为1
为什么需要新建节点呢?我们知道一个区间最多对应logn个线段树上的节点,那么直接连边就是log^2n条,通过中继点连边则是logn级别的,这样会少一些边
桦徒举栗
这里写图片描述
注意到这里是一个稠密图,考虑用堆优dij更优秀

Code


#include <stdio.h>
#include <string.h>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define fi first
#define se second

const int INF=0x3f3f3f3f;
const int N=500005;
const int E=3000005;

struct edge {int y,w,next;} e[E*10];
struct data {
    int x,v;
    bool operator <(const data &b) const {
        return v>b.v;
    }
} ;
struct treeNode {int l,r;} t[N*10];

std:: priority_queue <data> heap;

bool vis[E];

int dis[E],pos[N],tot,rec;
int ls[E],edCnt;

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void add_edge(int x,int y,int w) {
    e[++edCnt]=(edge) {y,w,ls[x]}; ls[x]=edCnt;
    // printf("%d %d %d\n", x,y,w);
}

void dijkstra(int st) {
    fill(dis,63); dis[st]=0;
    heap.push((data){st,dis[st]});
    rep(i,1,tot) {
        if (heap.empty()) return ;
        data now=heap.top(); heap.pop();
        while (!heap.empty()&&vis[now.x]) now=heap.top(),heap.pop();
        if (vis[now.x]) return ;
        vis[now.x]=1;
        for (int i=ls[now.x];i;i=e[i].next) {
            if (!vis[e[i].y]&&now.v+e[i].w<dis[e[i].y]) {
                dis[e[i].y]=now.v+e[i].w;
                heap.push((data){e[i].y,dis[e[i].y]});
            }
        }
    }
}

void dij(int p)
{
    for (int i=1;i<=tot;i++) dis[i]=INF;
    dis[pos[p]]=0;
    data u;u.x=pos[p];u.v=0;
    heap.push(u);
    for (int j=1;j<=tot;j++)
    {
        if (heap.empty()) break;
        data u=heap.top();
        heap.pop();
        while (!heap.empty()&&vis[u.x]) u=heap.top(),heap.pop();
        if (vis[u.x]) break;
        int x=u.x;vis[x]=1;
        for (int i=ls[x];i;i=e[i].next)
            if (!vis[e[i].y]&&dis[x]+e[i].w<dis[e[i].y])
            {
                dis[e[i].y]=dis[x]+e[i].w;
                u.x=e[i].y;u.v=dis[e[i].y];
                heap.push(u);
            }
    }
}

void modify(int now,int tl,int tr,int l,int r,int neww,int opt) {
    if (l>r) return ;
    if (tl==l&&tr==r) {
        if (opt) add_edge(now,neww,1);
        else add_edge(neww,now,1);
        return ;
    }
    int mid=(tl+tr)>>1;
    if (r<=mid) modify(t[now].l,tl,mid,l,r,neww,opt);
    else if (l>mid) modify(t[now].r,mid+1,tr,l,r,neww,opt);
    else {
        modify(t[now].l,tl,mid,l,mid,neww,opt);
        modify(t[now].r,mid+1,tr,mid+1,r,neww,opt);
    }
}

void build_tree(int &now,int tl,int tr,int opt) {
    t[now=++tot]=(treeNode) {0,0};
    if (!opt) add_edge(now,now-rec,0);
    if (tl==tr) {
        if (opt) pos[tl]=now;
        return ;
    }
    int mid=(tl+tr)>>1;
    build_tree(t[now].l,tl,mid,opt);
    build_tree(t[now].r,mid+1,tr,opt);
    if (opt) {
        add_edge(t[now].l,now,0);
        add_edge(t[now].r,now,0);
    } else {
        add_edge(now,t[now].l,0);
        add_edge(now,t[now].r,0);
    }
}

int main(void) {
    freopen("data.in","r",stdin);
    freopen("myp.out","w",stdout);
    int n=read(),m=read(),st=read();
    int root1,root2;
    build_tree(root1,1,n,1); rec=tot;
    build_tree(root2,1,n,0);
    rep(i,1,m) {
        int a=read(),b=read(),c=read(),d=read();
        modify(root1,1,n,a,b,++tot,1);
        modify(root2,1,n,c,d,tot,0);
        modify(root1,1,n,c,d,++tot,1);
        modify(root2,1,n,a,b,tot,0);
    }
    // dijkstra(pos[st]);
    dij(st);
    rep(i,1,n) printf("%d\n", dis[pos[i]]/2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值