1. 程式人生 > 其它 >P7516-[省選聯考2021A/B卷]圖函式【bfs】

P7516-[省選聯考2021A/B卷]圖函式【bfs】

正題

題目連結:https://www.luogu.com.cn/problem/P7516


題目大意

懶了,直接抄題意了
對於一張 \(n\) 個點 \(m\) 條邊的有向圖 \(G\)(頂點從 \(1 \sim n\) 編號),定義函式 \(f(u, G)\)

  1. 初始化返回值 \(cnt = 0\),圖 \(G'= G\)
  2. \(1\)\(n\) 按順序列舉頂點 \(v\),如果當前的圖 \(G';\) 中,從 \(u\)\(v\) 與從 \(v\)\(u\) 的路徑都存在,則將 \(cnt + 1\),並在圖 \(G';\) 中刪去頂點 \(v\) 以及與它相關的邊。
  3. \(2\)
    步結束後,返回值 \(cnt\) 即為函式值。

現在給定一張有向圖 \(G\),請你求出 \(h(G) = f(1, G) + f(2, G) + \cdots + f(n, G)\) 的值。

更進一步地,記刪除(按輸入順序給出的)第 \(1\)\(i\) 條邊後的圖為 \(G_i\)\(1 \le i \le m\)),請你求出所有 \(h(G_i)\) 的值。

\(1\leq n\leq 10^3,1\leq m\leq 2\times 10^5\)


解題思路

但凡一個不按慣性思維思考的方法都可以做出這道題

這個刪邊就很意義不明,反過來直接改成加邊就簡單很多。

然後還有一個問題就是是否刪點的判斷也是沒有必要的:

假設對於起點\(u\)能走到\(v\)\(v\)不能走到\(u\),那麼顯然並不存在一個節點\(x\)使得\(u\)能走到\(x\)\(x\)能走到\(u\)並且\(v\)是這些路徑的必經點,因為那麼\(v\)肯定在這個環上,那麼\(v\)顯然能走到\(u\)

所以現在\(f(u,G)\)能否統計\(v\)就變為了判斷是否存在一個\(u\rightarrow v\rightarrow u\)的環使得路徑上的所有點編號不小於\(min(u,v)\)

那麼不妨考慮一下兩個點對\((u,v)\)之間不斷加邊之後第一次產生貢獻的時間,每條邊的權值設為加入的時間,這個時間就是\(u\rightarrow v\rightarrow u\)

的一條不經過編號小於\(min(u,v)\)點的情況下最大權值最小的路徑。

這樣Flody就有\(O(n^3)\)的演算法了。

然後我們想了很久感覺最短路行不通,那麼此時就需要摒棄慣性思維的想法了,我們考慮另一個更暴力的做法,我們每次加邊然後暴力判斷每個點之間的聯通,發現這樣的時間複雜度是\(O(nm^2)\)的。

然後依舊的我們考慮另一種可能,我們最外層不列舉加邊,而是列舉需要統計答案的起點\(u\),然後每次加一條邊\((x,y)\),如果\(u\)能走到\(x\)且不能走到\(y\)那麼此時\(u\)能走到\(y\)了,從\(y\)開始\(bfs\)所有其他沒有走過的節點,需要注意的是走過的邊兩邊都是遍歷過的所以可以直接刪除。

這樣的時間複雜度就是\(O(n(n+m))\),可以通過本題。

需要卡常


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int N=1e3+10,M=2e5+10;
struct node{
	int to,next;
}a[M];
int n,m,f[N][N],g[N][N];
int ex[M],ey[M],ans[M];
vector<int> G[N],D[N];queue<int> q;
void bfs(int x,int s,int w){
	q.push(x);
	while(!q.empty()){
		int x=q.front();q.pop();f[s][x]=w;
		for(int i=0;i<G[x].size();i++)
			if(!f[s][G[x][i]])q.push(G[x][i]);
		G[x].clear();
	}
	return;
}
void bgs(int x,int s,int w){
	q.push(x);
	while(!q.empty()){
		int x=q.front();q.pop();g[s][x]=w;
		for(int i=0;i<D[x].size();i++)
			if(!g[s][D[x][i]])q.push(D[x][i]);
		D[x].clear();
	}
	return;
}
int main()
{
	scanf("%d%d",&n,&m);m++;
	for(int i=2;i<=m;i++)
		scanf("%d%d",&ex[i],&ey[i]);
	reverse(ex+2,ex+1+m);
	reverse(ey+2,ey+1+m);
	for(int x=1;x<=n;x++){
		for(int i=1;i<=n;i++)G[i].clear();
		for(int i=1;i<=n;i++)D[i].clear();
		f[x][x]=g[x][x]=1;
		for(int i=2;i<=m;i++){
			if(ex[i]<x||ey[i]<x)continue;
			if(f[x][ex[i]]&&!f[x][ey[i]])bfs(ey[i],x,i);
			else if(!f[x][ex[i]]&&!f[x][ey[i]])G[ex[i]].push_back(ey[i]);
			if(g[x][ey[i]]&&!g[x][ex[i]])bgs(ex[i],x,i);
			else if(!g[x][ey[i]]&&!g[x][ex[i]])D[ey[i]].push_back(ex[i]);
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)
			if(f[i][j]&&g[i][j])
				ans[max(f[i][j],g[i][j])]++;
	for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
	reverse(ans+1,ans+1+m);
	for(int i=1;i<=m;i++)
		printf("%d ",ans[i]);
	return 0;
}