线段树优化建边+最短路——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;
}

posted @ 2020-10-14 22:30  蒟蒻zyx_qwq  阅读(128)  评论(0编辑  收藏  举报