CF1062F Upgrading Cities

题目传送门

思路

考虑正反两遍拓扑,设 \(s_{0,i}/s_{1,i}\) 表示能到这个点的点数/从这个点出发能到的点数。

另一个显然的结论:正在拓扑的所有点之间是两两无法到达的。

设当前正在拓扑的点集为 \(v\)

  • \(v\) 中只含有一个数,显然这个 \(v\) 可以到达剩下没有拓扑的所有点。
  • \(v\) 中含有两个数,这两个数是互相无法到达的,接下来我们考虑这两个数能到达的点,设当前考虑的数为 \(x\),若某个与 \(x\) 有边点当前的入度为 \(1\),显然它只能从点 \(x\) 过去,无法从另一个点过去,这个时候我们就可以打个标记,另一个点不可能成为答案。
  • \(v\) 中含有三个数,显然这些数不可能成为答案。

就这样暴力拓扑即可。

代码

/*
A tree without skin will surely die. 
A man without face is invincible.
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
int const N=1e6+10;
int q[N],l,r,num,s[2][N],d[2][N],n;
vector<int>a[N],b[N];
inline void add(vector<int>&v,int type,vector<int>a[N]){
	if (v.size()==1){s[type][v[0]]=num-1;}
	else if (v.size()==2){
		s[type][v[0]]=s[type][v[1]]=num-2;
		for (auto j:a[v[0]]) if (d[type][j]==1) s[type][v[1]]=-1e8;
		for (auto j:a[v[1]]) if (d[type][j]==1) s[type][v[0]]=-1e8;
	}
	else{for (auto x:v) d[type][x]=-1e8;}
	for (auto x:v) q[++r]=x,--num;v.clear();
}
inline void toposort(vector<int>a[N],int type){
	vector<int>v;
	for (int i=1;i<=n;++i) if (!d[type][i]) v.push_back(i);
	num=n;l=0,r=-1;add(v,type,a);
	while (l<=r){
		int now=q[l++];
		for (auto j:a[now]) if (--d[type][j]==0) v.push_back(j);
		if (l>r) add(v,type,a);
	}
}
signed main(){
	int m;//读入
	while (m--){
		int u,v;//读入
		a[u].push_back(v);++d[0][v];
		b[v].push_back(u);++d[1][u];
	}
	toposort(a,0);
	toposort(b,1);
	int ans=0;
	for (int i=1;i<=n;++i) if (s[1][i]+s[0][i]>=n-2) ++ans;
	//输出
	return 0;
}
posted @ 2022-10-14 10:44  Tx_Lcy  阅读(66)  评论(0)    收藏  举报