[bzoj3073]Journeys

题目描述

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号国家能到任意一个国家。

注意:可能有重边

线段树优化

我们朴素想法是把图建出来跑最短路。
直接建图边数爆炸。
尝试数据结构优化连边。
可以建两颗线段树,分别称为A和B。
A和B中同一个节点B向A连边权为0。
A中儿子向父亲连边权为0。
B中父亲向儿子连边权为0。
对于一条边,可以分别在两颗线段树定位对应区间。
建一个新的辅助点,A中都向它连边权为1,它向B中连边权为1。
这样的话,原图联通性没有改变,而且新图上p在A上的叶子走到每个点在A上的叶子的最短路除以2就是原图最短路。
建完图跑Dij+heap

#include<cstdio>
#include<algorithm>
#include<queue>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=500000+10,maxtot=5*maxn+10,maxm=maxtot*7,inf=1000000000;
struct dong{
    int x,y;
    friend bool operator<(dong a,dong b){
        return a.y>b.y||a.y==b.y&&a.x<b.x;
    }
} zlt;
priority_queue<dong> dl;
int h[maxtot],go[maxm],dis[maxm],next[maxm],left[maxtot],right[maxtot],f[maxtot],leaf[maxn],sta[80];
bool bz[maxtot];
int i,j,k,l,r,t,n,m,p,now,tot,top,root1,root2;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y,int z){
    go[++top]=y;
    dis[top]=z;
    next[top]=h[x];
    h[x]=top;
}
void build(int &x,int l,int r,int p){
    x=++tot;
    if (l==r){
        if (p==1) leaf[l]=x;
        return;
    }
    int mid=(l+r)/2;
    build(left[x],l,mid,p);build(right[x],mid+1,r,p);
    if (p==1){
        add(left[x],x,0);
        add(right[x],x,0);
    }
    else{
        add(x,left[x],0);
        add(x,right[x],0);
    }
}
void connect(int x,int y,int l,int r){
    add(y,x,0);
    if (l==r) return;
    int mid=(l+r)/2;
    connect(left[x],left[y],l,mid);
    connect(right[x],right[y],mid+1,r);
}
void link(int x,int l,int r,int a,int b,int p){
    if (l==a&&r==b){
        if (p==1) add(x,tot,1);else add(tot,x,1);
        return;
    }
    int mid=(l+r)/2;
    if (b<=mid) link(left[x],l,mid,a,b,p);
    else if (a>mid) link(right[x],mid+1,r,a,b,p);
    else{
        link(left[x],l,mid,a,mid,p);
        link(right[x],mid+1,r,mid+1,b,p);
    }
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top) putchar('0'+sta[top--]);
    putchar('\n');
}
int main(){
    //freopen("data.in","r",stdin);
    n=read();m=read();p=read();
    build(root1,1,n,1);build(root2,1,n,-1);
    connect(root1,root2,1,n);
    fo(i,1,m){
        j=read();k=read();l=read();r=read();
        ++tot;
        link(root1,1,n,j,k,1);link(root2,1,n,l,r,-1);
        swap(j,l);swap(k,r);
        ++tot;
        link(root1,1,n,j,k,1);link(root2,1,n,l,r,-1);
    }
    t=leaf[p];
    f[t]=0;
    zlt.x=t;zlt.y=0;
    dl.push(zlt);
    fo(i,1,tot)
        if (i!=t){
            zlt.x=i;zlt.y=f[i]=inf;
            dl.push(zlt);
        }
    fo(i,1,tot-1){
        do{ 
            zlt=dl.top();
            dl.pop();
            now=zlt.x;
        }while (bz[now]);
        bz[now]=1;
        t=h[now];
        while (t){
            if (f[now]+dis[t]<f[go[t]]){
                f[go[t]]=f[now]+dis[t];
                zlt.x=go[t];zlt.y=f[go[t]];
                dl.push(zlt);
            }
            t=next[t];
        }
    }
    fo(i,1,n) write(f[leaf[i]]/2);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值