摩托羅拉 edge 輕奢版今日開啟預售:1 億畫素 30 倍融合變焦,2599 元起
在一個風和日麗的早晨, 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;
}