1. 程式人生 > 資訊 >摩托羅拉 edge 輕奢版今日開啟預售:1 億畫素 30 倍融合變焦,2599 元起

摩托羅拉 edge 輕奢版今日開啟預售:1 億畫素 30 倍融合變焦,2599 元起

CF1062F可謂難度相當大的一道拓撲排序題,應用了一個不常見的結論——隊內節點互相不可達。如果不看題解就能做出(顯然我不行),必定對拓撲排序有很深的理解。

在一個風和日麗的早晨, q0000000 同學竟然過了一道 黑題,可喜可賀。

UPDATE: 因為從首頁討論版進來時看到了思路,所以錯誤估計了難度——這玩意2900分,真見鬼。

這裡是題目傳送門和千辛萬苦後的AC記錄

一、 關於思路

要尋找這樣的點:圖上至多有一個點,既不能從它到達,也不能到達它

其實只需要關心前半句,因為看起來似乎是一個 dfs 啊、 bfs 啊,拓撲排序啊這樣的演算法。只要前半句解決了,後半句只需要反向做就可以了

怎麼辦?拓撲排序。

關於拓撲排序有這樣一個顯然的結論:同時處於佇列中的點互相不可達。我們知道,拓撲排序的原理是有層次的擴充套件,只有這個點出隊了,它的後繼點才可能被到達。

二、 關於寫法

計算每個點能到達哪些點,分類討論:

  1. 此時佇列中有 1 個點,那麼所有沒被廢棄的點都可以由它擴充套件到。設圖上還沒被廢棄的點數為 \(res\) ,點 \(i\) 除自己外能到達的點數為 \(f_i\) ,則有 \(f_i=f_i+res\)

  2. 此時佇列中有 2 個點,設前一個點為 \(x\) ,後一個點為 \(y\)\(x\) 如果還要合法,則未進隊的點必須都能被它擴充套件到。也就是說,不允許有一個點只能被 \(y\) 擴充套件到。我們從 \(y\) 出發遍歷,如果一個點入度為 0 ,不好意思, \(x\) 不合法了。如果沒有, \(f_x=f_x+res-1\)

  3. 此時佇列中有 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;
} 

THE END