您的位置:首页 > Web前端 > JavaScript

bzoj 1016: [JSOI2008]最小生成树计数【dfs+克鲁斯卡尔】

2018-06-30 23:05 681 查看

有一个性质就是组成最小生成树总边权值的若干边权总是相等的
这意味着按边权排序后在权值相同的一段区间内的边能被选入最小生成树的条数是固定的
所以先随便求一个最小生成树,把每段的入选边数记录下来
然后对于每一段dfs找合法方案即可,注意dfs中需要退回并查集,所以用不路径压缩的并查集
然后根据乘法定理,把每一段dfs后的结果乘起来即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1005,mod=31011;
int n,m,ans=1,sum,tot,cnt,l
,r
,c
,f
;
struct qwe
{
int u,v,w;
}a
;
bool cmp(const qwe &a,const qwe &b)
{
return a.w<b.w;
}
int zhao(int x)
{
return x==f[x]?x:zhao(f[x]);
}
void dfs(int q,int w,int k)
{
if(w==r[q]+1)
{
if(k==c[q])
sum++;
return;
}
int fu=zhao(a[w].u),fv=zhao(a[w].v);
if(fu!=fv)
{
f[fu]=fv;
dfs(q,w+1,k+1);
f[fu]=fu,f[fv]=fv;
}
dfs(q,w+1,k);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+1+m,cmp);
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
{
if(a[i].w!=a[i-1].w)
r[cnt]=i-1,l[++cnt]=i;
int fu=zhao(a[i].u),fv=zhao(a[i].v);
if(fu!=fv)
tot++,c[cnt]++,f[fu]=fv;
}
if(tot!=n-1)
{
puts("0");
return 0;
}
r[cnt]=m;
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=cnt;i++)
{
sum=0;
dfs(i,l[i],0);
ans=ans*sum%mod;
for(int j=l[i];j<=r[i];j++)
{
int fu=zhao(a[j].u),fv=zhao(a[j].v);
if(fu!=fv)
f[fu]=fv;
}
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: