CF1062F Upgrading Cities(拓扑排序)
在一个风和日丽的早晨, q0000000 同学竟然过了一道 黑题,可喜可贺。
UPDATE: 因为从首页讨论版进来时看到了思路,所以错误估计了难度——这玩意2900分,真见鬼。
一、 关于思路
要寻找这样的点:图上至多有一个点,既不能从它到达,也不能到达它。
其实只需要关心前半句,因为看起来似乎是一个 dfs 啊、 bfs 啊,拓扑排序啊这样的算法。只要前半句解决了,后半句只需要反向做就可以了。
怎么办?拓扑排序。
关于拓扑排序有这样一个显然的结论:同时处于队列中的点互相不可达。我们知道,拓扑排序的原理是有层次的扩展,只有这个点出队了,它的后继点才可能被到达。
二、 关于写法
计算每个点能到达哪些点,分类讨论:
-
此时队列中有 1 个点,那么所有没被废弃的点都可以由它扩展到。设图上还没被废弃的点数为 \(res\) ,点 \(i\) 除自己外能到达的点数为 \(f_i\) ,则有 \(f_i=f_i+res\) 。
-
此时队列中有 2 个点,设前一个点为 \(x\) ,后一个点为 \(y\) 。\(x\) 如果还要合法,则未进队的点必须都能被它扩展到。也就是说,不允许有一个点只能被 \(y\) 扩展到。我们从 \(y\) 出发遍历,如果一个点入度为 0 ,不好意思, \(x\) 不合法了。如果没有, \(f_x=f_x+res-1\) 。
-
此时队列中有 3 个点,它们都不互相可达,已经不合法了,不用管。
反向同理——建反图,跑两遍拓扑排序即可。
三、 关于代码
在出队位置进行判断,更新 \(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;
}