题面描述
给定一个n个点、m条边的无向图,求其最小生成树的个数。相同边权的边不会超过10条。
思维难度:提高+;代码难度:提高+;
题解:
先给出两个引理:
1.克鲁斯卡尔求最小生成数实际上是分成很多个阶段的,你可以感受到:很多边权相同的边因为排序顺序不同,导致它们被访问的顺序不同。但每处理完一个边权相同的边集,当前图的一些性质(连通块数、边数、代价数)是一样的,并不会因为同边权集中的边的排序顺序改变而改变。换句话说,图的本质可能会变,但它的“表现”是不变的。
2.不同的阶段产生的边,对于每棵最小生成树,是恒定的,且每个阶段在保证正确的如何选择,对后面的选择没有影响。统计答案时,应该使用乘法原理。
那么我们接下来有两种方法:Matrix Tree 或者 爆搜2333
注意到题面中写的“相同边权的边不超过10条”,直接指数级爆搜选不选就可以了。并查集时不能用路径压缩,因为你要有撤销功能。
提交次数:2次 第一次是因为没删调试代码。B站不显示错误输出真是好坑啊。
这题启示我们:省选题也有指数级爆搜的舞台(ohhh)。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
using namespace std;
const int Mod = 31011;
struct Node{int u,v,w;}E[Mod];
int n,m,Ans,flag,fa[Mod],vis[Mod],ans;
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
LL gl()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
bool cmp(const Node &a,const Node &b){return a.w<b.w;}
int find(int x){return fa[x]==x?x:find(fa[x]);}
void dfs(int dep,int ch,int nu,int ed,int num)
{
if(nu==num){ans++;return;}
if(dep==ed)return;
dfs(dep+1,0,nu,ed,num);
if(find(E[dep+1].u)!=find(E[dep+1].v))
{
int x=find(E[dep+1].v);
fa[x]=find(E[dep+1].u);
dfs(dep+1,1,nu+1,ed,num);
fa[x]=x;
}
}
int solve(int l,int r)
{
int num=0,fat[110];ans=0;
for(int i=1;i<=n;++i)fat[i]=fa[i];
for(int i=l;i<=r;++i)
if(find(E[i].u)!=find(E[i].v))
num++,fa[find(E[i].v)]=find(E[i].u);
for(int i=1;i<=n;++i)swap(fa[i],fat[i]);
dfs(l-1,0,0,r,num);
for(int i=1;i<=n;++i)fa[i]=fat[i];
return ans;
}
void work()
{
sort(E+1,E+m+1,cmp);
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1,num=0;i<=m;++i)
{
int x=find(E[i].u),y=find(E[i].v);
if(x!=y)fa[y]=x,num++;
if(num==n-1)break;
if(i==m)Ans=0;
}
for(int i=1;i<=n;++i)fa[i]=i;
for(int i=1,last=1;i<=m;++i)
{
if(E[i].w!=E[last].w)
{
Ans*=solve(last,i-1);
Ans%=Mod;last=i;
}
if(i==m){Ans*=solve(last,m);}
}
}
int main()
{
n=gi();m=gi();Ans=1;
for(int i=1;i<=m;++i)
E[i].u=gi(),E[i].v=gi(),E[i].w=gi();
work();printf("%d",Ans%Mod);
return 0;
}