CF1062F Upgrading Cities(拓扑排序)

在一个风和日丽的早晨, q0000000 同学竟然过了一道 黑题,可喜可贺。

UPDATE: 因为从首页讨论版进来时看到了思路,所以错误估计了难度——这玩意2900分,真见鬼。

这里是题目传送门和千辛万苦后的AC记录

一、 关于思路

要寻找这样的点:图上至多有一个点,既不能从它到达,也不能到达它

其实只需要关心前半句,因为看起来似乎是一个 dfs 啊、 bfs 啊,拓扑排序啊这样的算法。只要前半句解决了,后半句只需要反向做就可以了

怎么办?拓扑排序。

关于拓扑排序有这样一个显然的结论:同时处于队列中的点互相不可达。我们知道,拓扑排序的原理是有层次的扩展,只有这个点出队了,它的后继点才可能被到达。

二、 关于写法

计算每个点能到达哪些点,分类讨论:

  1. 此时队列中有 1 个点,那么所有没被废弃的点都可以由它扩展到。设图上还没被废弃的点数为 \(res\) ,点 \(i\) 除自己外能到达的点数为 \(f_i\) ,则有 \(f_i=f_i+res\)

  2. 此时队列中有 2 个点,设前一个点为 \(x\) ,后一个点为 \(y\)\(x\) 如果还要合法,则未进队的点必须都能被它扩展到。也就是说,不允许有一个点只能被 \(y\) 扩展到。我们从 \(y\) 出发遍历,如果一个点入度为 0 ,不好意思, \(x\) 不合法了。如果没有, \(f_x=f_x+res-1\)

  3. 此时队列中有 3 个点,它们都不互相可达,已经不合法了,不用管。

反向同理——建反图,跑两遍拓扑排序即可。

image

三、 关于代码

在出队位置进行判断,更新 \(f\)

if(q.empty()) f[u]+=res;
else if(q.size()==1){
	for(int i=h[q.front()];i;i=e[i].nxt){
		int v=e[i].v;
		if(ind[v]==1) goto t;
	}
   	 f[u]+=res-1;t:;
}

发现 goto 比一堆 flag 好用。

UPDATE: goto 看似方便,实际上削弱了代码可读性(上蹿下跳的),应当避免使用。多用几个 flag 也是好的。

四、 关于爆零

这次没有爆零。

应当注意的是,手写队列时,如果初始化 queue(){l=r=1;} ,那返回的队头应该是 q[l+1]

五、 AC 代码

#include<stdio.h>
#include<string.h>
const int N=3e6+10;
inline int rd(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f^=1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?x:-x;
}
struct queue{
	int l,r,d[N];
	queue(){l=r=1;}
	inline void push(int x){d[++r]=x;}
	inline void pop(){++l;}
	inline bool empty(){return l>=r;}
	inline int size(){return r-l;}
	inline int front(){return d[l+1];}
}q;
struct edge{int v,nxt;}e[N<<1];
int tot,h[N],u[N],v[N],f[N],ind[N];
inline void add(int u,int v){
	e[++tot]=(edge){v,h[u]};
	h[u]=tot;
}
int main(){
	int n=rd(),res=n,m=rd();
	for(int i=1;i<=m;++i){
		u[i]=rd(),v[i]=rd();
		add(u[i],v[i]);
		++ind[v[i]];
	}
	for(int i=1;i<=n;++i)
		if(!ind[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
//		printf("u=%d\n",u);
		--res;
//		if(u==4) printf("nowsize=%d\n",q.size());
		if(q.empty()) f[u]+=res;
		else if(q.size()==1){
			for(int i=h[q.front()];i;i=e[i].nxt){
				int v=e[i].v;
				if(ind[v]==1) goto s;
			}
			f[u]+=res-1;s:;
		}
		for(int i=h[u];i;i=e[i].nxt){
			int v=e[i].v;
			--ind[v];
			if(!ind[v]) q.push(v);
		}
	}
//	printf("f[4]=%d\n",f[4]); 
	tot=0,res=n;
	memset(h,0,sizeof(h));
	memset(ind,0,sizeof(ind));
	for(int i=1;i<=m;++i){
		add(v[i],u[i]);
		++ind[u[i]];
	}
	for(int i=1;i<=n;++i)
		if(!ind[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		--res;
		if(q.empty()) f[u]+=res;
		else if(q.size()==1){
			for(int i=h[q.front()];i;i=e[i].nxt){
				int v=e[i].v;
				if(ind[v]==1) goto t;
			}
			f[u]+=res-1;t:;
		}
		for(int i=h[u];i;i=e[i].nxt){
			int v=e[i].v;
			--ind[v];
			if(!ind[v]) q.push(v);
		}
	}
//	printf("f[4]=%d\n",f[4]); 
	int ans=0;
	for(int i=1;i<=n;++i)
		if(f[i]>=n-2){
			++ans;
//			printf("[%d] ",i);
		}
	printf("%d\n",ans);
	return 0;
} 

THE END

posted @ 2021-08-13 21:01  q0000000  阅读(25)  评论(0编辑  收藏  举报