P4700-[CEOI2011]Traffic【tarjan,dp】

正题

题目链接:https://www.luogu.com.cn/problem/P4700


题目大意

\(A\times B\)的网格上有\(n\)个点,然后\(m\)条有向/无向边连接成平面图,求最左边每个点能到达的最右边点的数量。

\(1\leq A,B\leq 10^9,1\leq n\leq 3\times 10^5,1\leq m\leq 9\times 10^5\)


解题思路

突破点肯定是平面图,考虑假设对于左边两个点\(a,b\),右边两个\(A,B\)\(y\)坐标递增,显然如果\(a\)能走到\(B\)且不能走到\(A\)那么\(b\)一定不能走到\(A\)

也就是说每个点能到右边一定是一个区间上的点,先\(tarjan\)缩点一下\(dp\)出这个区间就好了。

需要注意的是如果一个右边的点无法被任何左边的点走到那么它不能被统计在区间里。

时间复杂度:\(O(n\log n)\)(排序)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
const int N=3e5+10;
int n,m,A,B,cnt,cot,pnt,bnt;
int X[N],Y[N],p[N],b[N],l[N],r[N];
int dfn[N],low[N],col[N],in[N];
queue<int> q;stack<int> s;
vector<int> G[N],T[N];
bool ins[N],v[N];
void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	s.push(x);ins[x]=1;
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y])
			low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		col[x]=++cot;
		while(s.top()!=x){
			col[s.top()]=cot;
			ins[s.top()]=0;s.pop();
		}
		ins[x]=0;s.pop();
	}
	return;
}
void dfs(int x){
	if(v[x])return;v[x]=1;
	for(int i=0;i<G[x].size();i++)
		dfs(G[x][i]);
}
void Topsort(){
	for(int i=1;i<=cot;i++)
		if(!in[i])q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=0;i<T[x].size();i++){
			int y=T[x][i];in[y]--;
			l[y]=min(l[y],l[x]);
			r[y]=max(r[y],r[x]);
			if(!in[y])q.push(y);
		}
	}
	return;
}
bool cmp(int x,int y)
{return Y[x]>Y[y];}
int main()
{
	scanf("%d%d%d%d",&n,&m,&A,&B);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&X[i],&Y[i]);
	for(int i=1;i<=m;i++){
		int x,y,k;
		scanf("%d%d%d",&x,&y,&k);
		G[x].push_back(y);
		if(k!=1)G[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
		if(X[i]==0)dfs(i);
	for(int i=1;i<=n;i++){
		if(X[i]==0)p[++pnt]=i;
		else if(X[i]==A&&v[i])b[++bnt]=Y[i];
	}
	sort(b+1,b+1+bnt);
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int i=1;i<=cot;i++)l[i]=bnt+1,r[i]=0;
	for(int i=1;i<=n;i++)
		if(X[i]==A&&v[i]){
			Y[i]=lower_bound(b+1,b+1+bnt,Y[i])-b;
			l[col[i]]=min(l[col[i]],Y[i]);
			r[col[i]]=max(r[col[i]],Y[i]);
		}
	for(int x=1;x<=n;x++)
		for(int i=0;i<G[x].size();i++){
			int y=G[x][i];
			if(col[x]==col[y])continue;
			T[col[y]].push_back(col[x]);in[col[x]]++;
		}
	Topsort();
	sort(p+1,p+1+pnt,cmp);
	for(int i=1;i<=pnt;i++){
		int x=col[p[i]];
		if(!r[x]){puts("0");continue;}
		printf("%d\n",r[x]-l[x]+1);
	}
	return 0;
}
posted @ 2021-09-27 19:02  QuantAsk  阅读(78)  评论(0编辑  收藏  举报