圖論-生成樹-最小生成樹計數

我的天,改一個晚上才改出來,一個及其細微難以發現的邏輯錯誤:在做最小生成樹順便保留並查集狀態的時候,不論每次是否處理的是新的種類的邊(離散化後的),都應當先將fa拷貝到nowfa當中去,否則將會出現一種邊沒有被使用過(因爲加入後會形成環),從而其相對應的nowfa均爲0,導致在dfs判斷所選則的邊會不會形成環時得出有環的結論(因爲fa[a]、fa[b]都是0),進而引起方案數出現0的情況。

#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
using namespace std;
typedef long long ll;
const int inf=1e9+10,mod=31011,N=666,M=2222;
const double eps=1e-6;
struct edge{
	int a,b,w;
	bool friend operator < (edge a,edge b){
		return a.w<b.w;
	}
}e[M];
int n,m,cnt[M],fa[N];//cnt[i]表示權值排第i的邊在最小生成樹用到了幾條。
int nowfa[M][N],st[N],ret;
ll ans=1;
int getf(int v){ return fa[v]==v?v:fa[v]=getf(fa[v]);}
bool selected[M];
int tmpfa[N];
int tmpgetf(int v) { return tmpfa[v]==v?v:tmpfa[v]=tmpgetf(tmpfa[v]);}
void dfs(int v,int pos,int sel){
	if(sel==cnt[v]){
		memcpy(tmpfa,nowfa[v-1],sizeof(nowfa[v-1]));
		rep(i,st[v],pos) if(selected[i]){
			int faa=tmpgetf(e[i].a),fab=tmpgetf(e[i].b);
			if(faa==fab) return;
			tmpfa[fab]=faa;
		}
		ret++;
		return;
	}
	if(pos>=st[v+1])return;
	dfs(v,pos+1,sel);
	selected[pos]=true;
	dfs(v,pos+1,sel+1);
	selected[pos]=false;
}
void process(int v){
	ret=0;
	if(cnt[v]==0) return;
	dfs(v,st[v],0);
	ans=(ans*(ll)ret)%mod;
	return;  
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
	cin>>n>>m;
	rep(i,1,m) cin>>e[i].a>>e[i].b>>e[i].w;
	sort(e+1,e+1+m);
	int now=0,num=0;
	rep(i,1,m){//離散化處理 
		if(e[i].w!=now){
			now=e[i].w;
			e[i].w=++num;
			st[e[i].w]=i;
		}else e[i].w=num;
	}
	st[e[m].w+1]=m+1;
	rep(i,1,n) fa[i]=i;
	int last=0,tot=0;
	rep(i,1,m){
		int faa=getf(e[i].a),fab=getf(e[i].b);
		memcpy(nowfa[last],fa,sizeof(fa));
		last=e[i].w;
		if(faa==fab) continue;
		cnt[e[i].w]++; fa[fab]=faa; tot++;
	}
	if(tot<n-1){
		cout<<0;
		return 0;
	}
	rep(i,1,num) process(i);//處理權值爲i的邊 
	cout<<ans;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章