1. 程式人生 > 實用技巧 >線段樹優化建邊+最短路——bzoj3073[Pa2011]Journeys

線段樹優化建邊+最短路——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\)

保證\(P\)號國家能到任意一個國家。

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;
}