P4208 [JSOI2008]最小生成树计数
Posted y2823774827y
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}/*
*/
以上是关于P4208 [JSOI2008]最小生成树计数的主要内容,如果未能解决你的问题,请参考以下文章