CF1062F Upgrading Cities
这道题一眼看上去可能没什么思路至少我是这样的,但仔细思考后,就会发现可以用拓扑排序。
拓扑排序有一个性质,就是任意时刻在队列里的点都不能互相到达这不是显然吗,用这个性质,我们就可以求出每个点能到达的节点个数了。
但是重要的节点和次重要的节点的定义是互相到达,于是我们要将原图的每条边反过来存一次图,暂且叫它“反图”,然后把反图做一遍拓扑排序,再把两次拓扑排序求出来的每个点可以到达的节点个数加起来,就得到了每个点可以和其它点互相到达的节点个数。
最后,把每个点能到达的节点个数判断一遍,如果一个点的个数$≥n-2$,那么这个点就是一个重要的节点或次重要的节点。
#include<iostream> #include<cstdio> #include<vector> #include<queue> using namespace std; const int maxn=300010; vector<int>G1[maxn],G2[maxn]; //G1[]:原图的邻接表,G2[]:反图的邻接表 int n,m,ans; int in1[maxn],in2[maxn]; //in1[]:原图每个点的入度,in2[]:反图每个点的入度 int d[maxn]; //每个点能到达的节点个数 int read() //快读 { int x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x; } void tp1() //拓扑排序原图 { queue<int>que; int sum=n; //目前能到达的节点个数 for(int i=1;i<=n;i++) if(!in1[i]) //入度为零就入队 { que.push(i); sum--; //因为i没有入度,所以能到达的节点个数减一 } while(!que.empty()) { int t=que.front(); que.pop(); if(que.empty()) d[t]+=sum; //如果队列为空,那么t可以到达剩下所有的节点 if(que.size()==1) //如果队列里剩一个节点,那么判断删去这个节点后t能否到达其余所有的节点 { int tt=que.front(); //tt:t不能到达的点 int flag=1; //flag=1:t能到达其余所有的节点,flag=0:不能 for(int i=0;i<G1[tt].size();i++)//枚举tt能到达的点 if(in1[G1[tt][i]]==1) //如果能到达G1[tt][i]的点只有一个,那么t就不能到达G1[tt][i] { flag=0; break; } if(flag==1) d[t]+=sum; } //拓扑排序入队 for(int i=0;i<G1[t].size();i++) { int c=G1[t][i]; if(!(--in1[c])) { que.push(c); sum--; //因为c入队了,所以后面的节点就都到不了c了 } } } return; } void tp2() //拓扑排序反图 { queue<int>que; int sum=n; //目前能到达的节点个数 for(int i=1;i<=n;i++) if(!in2[i]) //入度为零就入队 { que.push(i); sum--; //因为i没有入度,所以能到达的节点个数减一 } while(!que.empty()) { int t=que.front(); que.pop(); if(que.empty()) d[t]+=sum; //如果队列为空,那么t可以到达剩下所有的节点 if(que.size()==1) //如果队列里剩一个节点,那么判断删去这个节点后t能否到达其余所有的节点 { int tt=que.front(); //tt:t不能到达的点 int flag=1; //flag=1:t能到达其余所有的节点,flag=0:不能 for(int i=0;i<G2[tt].size();i++)//枚举tt能到达的点 if(in2[G2[tt][i]]==1) //如果能到达G1[tt][i]的点只有一个,那么t就不能到达G1[tt][i] { flag=0; break; } if(flag==1) d[t]+=sum; } //拓扑排序入队 for(int i=0;i<G2[t].size();i++) { int c=G2[t][i]; if(!(--in2[c])) { que.push(c); sum--; //因为c入队了,所以后面的节点就都到不了c了 } } } return; } int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(); G1[u].push_back(v); in1[v]++; G2[v].push_back(u); in2[u]++; } tp1(); tp2(); //如果第i个点能到达的节点个数≥n-2,那么i是重要的节点或此重要的节点 for(int i=1;i<=n;i++) if(d[i]>=n-2) ans++; printf("%d\n",ans); return 0; }
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$