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

浙公网安备 33010602011771号