bzoj 1016: [JSOI2008]最小生成树计数dfs+克鲁斯卡尔
Posted lokiii
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1016: [JSOI2008]最小生成树计数dfs+克鲁斯卡尔相关的知识,希望对你有一定的参考价值。
有一个性质就是组成最小生成树总边权值的若干边权总是相等的
这意味着按边权排序后在权值相同的一段区间内的边能被选入最小生成树的条数是固定的
所以先随便求一个最小生成树,把每段的入选边数记录下来
然后对于每一段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[N],r[N],c[N],f[N];
struct qwe
{
int u,v,w;
}a[N];
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
",ans);
return 0;
}
以上是关于bzoj 1016: [JSOI2008]最小生成树计数dfs+克鲁斯卡尔的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)
BZOJ1016:[JSOI2008]最小生成树计数——题解