BZOJ1016 最小生成树计数

  题面描述

  最小生成树计数

  给定一个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;
}

 

 

 

  

posted @ 2017-03-21 16:16  Fenghr  阅读(...)  评论(... 编辑 收藏