線段樹優化建邊+最短路——bzoj3073[Pa2011]Journeys
Description
\(Seter\)建造了一個很大的星球,他準備建造\(N\)個國家和無數雙向道路。\(N\)個國家很快建造好了,用\(1..N\)編號,但是他發現道路實在太多了,他要一條條建簡直是不可能的!於是他以如下方式建造道路:\((a,b),(c,d)\)表示,對於任意兩個國家x,y,如果\(a\leq x\leq b,c\leq y\leq d\),那麼在\(x,y\)之間建造一條道路。\(Seter\)保證一條道路不會修建兩次,也保證不會有一個國家與自己之間有道路。
\(Seter\)好不容易建好了所有道路,他現在在位於\(P\)號的首都。\(Seter\)想知道\(P\)號國家到任意一個國家最少需要經過幾條道路。當然,\(Seter\)
Input
第一行三個數\(N,M,P\)。\(N\leq 500000,M\leq 100000\)。
後M行,每行4個數\(A,B,C,D\)。\(1\leq A\leq B\leq N,1\leq C\leq D\leq N\)。
Output
\(N\)行,第\(i\)行表示\(P\)號國家到第\(i\)個國家最少需要經過幾條路。顯然第\(P\)行應該是0。
Sample Input
5 3 4
1 2 4 5
5 5 4 4
1 1 3 3
Sample Output
1
1
2
0
1
solution
一個顯然的暴力思路就是一個個點連邊。但是這樣大資料一定裂開。我們考慮線段樹優化建邊(線段樹可以完成區間操作,題目給出的就是一整個區間,對於線段樹來說是非常友好的)。
線段樹優化建邊
我們定義:
進樹:一顆線段樹從父親到兒子連邊權為0的邊
出樹:一顆線段樹從兒子到父親連邊權為0的邊
超級點:既不屬於進樹也不屬於出樹,用於連線進樹與出樹的點
那麼我們對於每一個要連的區間\((a,b),(c,d)\)都先將出樹中對應區間連邊權為0的有向邊到超級點,再從超級點連邊權為1的有向邊到進樹中,這裡需要兩個超級點,如果用同一個超級點的話,會有原來不能互相到達的點卻互相到達的情況。
大概如圖:圖源
為什麼要從進樹連邊到出樹?因為從給定的起點出發可能需要經過別的點才能到某些點。
剩下的就是一個簡單的\(dijkstra\)啦
Code
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #define mk make_pair #define lc k<<1 #define rc k<<1|1 using namespace std; const int N=5e5+5,M=1e7+5; struct E { int to,nxt,w; }e[N*30]; int n,m,p,head[M],cnt,tot,sup,vis[M],dis[M]; inline int read () { int res=0,fl=1; char ch; while ((ch=getchar())&&(ch<'0'||ch>'9')) if (ch=='-') fl=-1; res=ch^48; while ((ch=getchar())&&ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^48); return res*fl; } inline void add (int x,int y,int z) { e[++cnt]=(E){y,head[x],z}; head[x]=cnt; } struct node { int tre[N<<2]; inline void build (int k,int l,int r,int ty) { tre[k]=++tot; if (l==r) return; int mid=(l+r)>>1; build(lc,l,mid,ty); build(rc,mid+1,r,ty); if (ty==0) add(tre[k],tre[lc],0),add(tre[k],tre[rc],0); else add(tre[lc],tre[k],0),add(tre[rc],tre[k],0); } inline void update (int k,int l,int r,int x,int y,int v,int ty) { if (x<=l&&r<=y) { if (ty) add(tre[k],sup,v); else add(sup,tre[k],v); return; } int mid=(l+r)>>1; if (x<=mid) update(lc,l,mid,x,y,v,ty); if (y>mid) update(rc,mid+1,r,x,y,v,ty); } inline int get (int k,int l,int r,int x) { if (l==r&&l==x) return tre[k]; int mid=(l+r)>>1; if (x<=mid) return get(lc,l,mid,x); else return get(rc,mid+1,r,x); } }t_in,t_out; inline void build2 (int k,int l,int r) { add(t_in.tre[k],t_out.tre[k],0); if (l==r) return; int mid=(l+r)>>1; build2(lc,l,mid); build2(rc,mid+1,r); } inline void dijkstra (int st) { priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q; memset(dis,0x3f,sizeof(dis)); dis[st]=0; q.push(mk(dis[st],st)); while (!q.empty()) { int x=q.top().second; q.pop(); if (vis[x]) continue; vis[x]=1; for (int i=head[x];i;i=e[i].nxt) { int v=e[i].to; if(vis[v]) continue; if (dis[v]>dis[x]+e[i].w) { dis[v]=dis[x]+e[i].w; q.push(mk(dis[v],v)); } } } } inline void print (int k,int l,int r) { if (l==r) { printf("%d\n",dis[t_in.tre[k]]); return; } int mid=(l+r)>>1; print(lc,l,mid); print(rc,mid+1,r); } int main () { #ifndef ONLINE_JUDGE freopen("3073.in","r",stdin); freopen("3073.out","w",stdout); #endif n=read();m=read();p=read(); // cout<<n<<" "<<m<<" "<<p<<endl; t_in.build(1,1,n,0); // cout<<"*"<<tot<<endl; t_out.build(1,1,n,1); sup=tot; // cout<<"!"<<tot<<endl; for (int i=1,a,b,c,d;i<=m;i++) { a=read();b=read();c=read();d=read(); sup++; t_in.update(1,1,n,a,b,1,0); t_out.update(1,1,n,c,d,0,1); sup++; t_in.update(1,1,n,c,d,1,0); t_out.update(1,1,n,a,b,0,1); } build2(1,1,n); // cout<<"!"<<endl; // int pos=t_out.get(1,1,n,p); int pos1=t_in.get(1,1,n,p); // add(pos,pos1,0); // cout<<pos<<endl; dijkstra(pos1); print(1,1,n); return 0; }