图论-生成树-最小生成树计数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TengWan_Alunl/article/details/83246005

我的天,改一个晚上才改出来,一个及其细微难以发现的逻辑错误:在做最小生成树顺便保留并查集状态的时候,不论每次是否处理的是新的种类的边(离散化后的),都应当先将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;
}

猜你喜欢

转载自blog.csdn.net/TengWan_Alunl/article/details/83246005