P4208 [JSOI2008]最小生成树计数

题目

P4208 [JSOI2008]最小生成树计数

以前一直以为JSOI的题应该比较水吧,果然还是我太ruo了

做法

两个性质
\(1.\):最小生成树中同一权值的边数一定
\(2.\):最小生成树中同一权值的边连接后联通块状态相同

利用这两个性质,开两个数组维护所属联通块(并查集),每次处理同一边权的点

假设我们上一次权值的记录为\(last\),实时更新的为\(fa\)

处理此权值边:如果在last中的两点没联通那标记一下这是可用的,让这些点形成树,存在有些在处理此边权时联通,会形成环,这些不是不能选,因为我们用的方法是分治形成树,用这些边单独形成一棵树,显然所有权值的边最后会形成一棵大树,所以我们这里记录祖先是用的联通之前的,然后更新祖先是实时更新的(可以理解为处理一个权值后就把那些缩点)

然后处理同一权值,这些块可能不止一个就分开计算这些边能形成的树,矩阵树就行了

My complete code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<ctime>
#include<vector>
using namespace std;
typedef int LL;
const LL maxn=200;
const LL p=31011;
inline LL Read(){
	LL 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<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}
struct node{
	LL u,v,d;
}e[maxn*maxn];
LL n,m;
LL g[maxn][maxn],D[maxn][maxn];
inline bool cmp(node x,node y){
	return x.d<y.d;
}
LL Get_fa(LL u,LL *fa){
	return fa[u]=(fa[u]==u)?u:Get_fa(fa[u],fa);
}
inline LL Calc(LL N){
	LL tr(0),ret(1);
	for(LL i=1;i<=N;++i){
		for(LL j=i+1;j<=N;++j){
			while(D[j][i]){
				LL tmp=D[i][i]/D[j][i];
				for(LL k=1;k<=N;++k){
					D[i][k]=(D[i][k]-tmp*D[j][k]%p+p)%p;
					swap(D[i][k],D[j][k]);
				}
				tr^=1;
			}
		}
		if(D[i][i]==0)
		    return 0;
		ret=ret*D[i][i]%p;
	}
	if(tr)
	    ret=p-ret;
	return ret;
}
LL ans=1;
LL fa[maxn],last[maxn];
bool visit[maxn];
vector<LL> belong[maxn];
inline void Solve(){
	for(LL i=1;i<=n;++i)
	    if(visit[i])
	        belong[Get_fa(i,fa)].push_back(i),
	        visit[i]=false;
	LL ret(1);
	for(LL i=1;i<=n;++i){
		LL size(belong[i].size());
		if(size<=1)
		    continue;
		memset(D,0,sizeof(D));
		for(LL j=1;j<=size;++j){
			LL u(belong[i][j-1]);
			for(LL k=j+1;k<=size;++k){
				LL v(belong[i][k-1]);
				if(g[u][v]){
					D[j][k]=D[k][j]=(p-g[u][v])%p,
					D[j][j]+=g[u][v],D[k][k]+=g[u][v];
				}
			}
		}
		ans=(ans*Calc(size-1))%p;
		for(LL j=1;j<=size;++j)
			last[belong[i][j-1]]=i;
	}
	for(LL i=1;i<=n;++i)
	    belong[i].clear(),
	    fa[i]=last[i]=Get_fa(i,last);
}
int main(){
	n=Read(),m=Read();
	for(LL i=1;i<=m;++i)
	    e[i].u=Read(),e[i].v=Read(),e[i].d=Read();
	sort(e+1,e+1+m,cmp);
	e[0]=e[1];
	for(LL i=1;i<=n;++i)
	    last[i]=fa[i]=i;
	for(LL i=1;i<=m;++i){
		if(e[i].d^e[i-1].d){
		    Solve();
		    memset(g,0,sizeof(g));
		}
		LL u(e[i].u),v(e[i].v);
		LL fu(Get_fa(u,last)),fv(Get_fa(v,last)) ;
		if(fu^fv){
			visit[fu]=visit[fv]=true;
			++g[fu][fv]; ++g[fv][fu];
			fa[Get_fa(fu,fa)]=Get_fa(fv,fa);
		}
	}Solve();
	for(LL i=2;i<=n;++i)
	    if(last[i]^last[i-1]){
	    	printf("0");
	    	return 0;
		}
	printf("%d",ans);
	return 0;
}/*
*/
posted @ 2019-01-15 07:10  y2823774827y  阅读(163)  评论(0编辑  收藏  举报