最小生成树计数——JSOI2008

略有难度的最小生成树问题:
先排序算出某一长度的边有几条,放到tong[],再用krus求某一长度的边需要几条,xu[],再用枚举的方法举例出某一长度的符合条件为几条(再枚举下一种边时。把边覆盖上),分别为f1,f2,f3……再把f1,f2,f3相乘……(当然乘的过程不要忘了mod31011)
ps:比较郁闷的是数组开小了,一直wa,稍微开大点TLE,再开大,AC……我就奇怪为什么别人的程序数组开小没问题,囧……
其实还有一种矩阵的解法,速度更快……自己看看论文吧
View Code
#include<stdio.h>
#include
<iostream>
#include
<algorithm>
using namespace std;

int n,m,f[19909];
int tempf[19909];
int tong[10999];//保存相同边的个数
int xu[19909];//保存需要相同边几条
struct data
{
int fr;
int to;
int w;
}edge[
109909];

bool cmp(data a,data b)
{
return a.w<b.w;
}

int find(int pos)
{
if(f[pos]==-1)
return pos;
return f[pos]=find(f[pos]);
}

int un(int a,int b)
{
int fa=find(a),fb=find(b);
if(fa==fb) return 0;
f[fa]
=fb;return 1;
}

void krus()
{
int i,j,k;
for(i=1;i<=n;i++)
{
f[i]
=-1;
}
for(i=1;i<=m;i++)
{
xu[i]
=0;tong[i]=0;
}
sort(
&edge[1],&edge[1+m],cmp);
int kind=1,chang=edge[1].w;//需要不同边种

int tre=0;
for(i=1;i<=m;i++)
{
if(edge[i].w==chang)//同种长度的边需要几条
{
tong[kind]
++;
}
else
{
kind
++;
chang
=edge[i].w;
tong[kind]
++;
}

if(un(edge[i].fr,edge[i].to)==1)
{
xu[kind]
++;
tre
++;
}
}

if(tre!=(n-1))
{
printf(
"0\n");return ;
}

int bian=0,kbian=0,cc,all;
for(i=1;i<=n;i++)
{
f[i]
=-1;
tempf[i]
=-1;
}
all
=1;
int shu[20],sadd;
for(i=1;i<=kind;i++)//一共有kind种长度
{
cc
=0;
for(j=1;j<=(1<<tong[i]);j++)
{
sadd
=1;
for(k=0;k<tong[i];k++)
{
if(j&(1<<k))
{
sadd
++;
}
}
if(sadd-1!=xu[i])continue;

sadd
=1;
for(k=0;k<tong[i];k++)
{
if(j&(1<<k))
{
shu[sadd]
=k+1;
sadd
++;
}
}
for(k=1;k<=n;k++)
{
tempf[k]
=f[k];
}
//保存临时点
int add=0;//是否等于xu[i]
for(k=1;k<=xu[i];k++)
{
if(un(edge[kbian+shu[k]].fr,edge[kbian+shu[k]].to)==1)
{
add
++;
}
}

for(k=1;k<=n;k++)
{
f[k]
=tempf[k];
}
if(add==xu[i])
cc
++;

}
for(j=1;j<=tong[i];j++)//更新f[]
{
un(edge[j
+kbian].fr,edge[j+kbian].to);
}

kbian
+=tong[i];
all
=all*cc%31011;
}

printf(
"%d\n",all);
}

int main()
{
int i;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=1;i<=m;i++)
{
scanf(
"%d%d%d",&edge[i].fr,&edge[i].to,&edge[i].w);
}
krus();
}

return 0;
}
原文地址:https://www.cnblogs.com/huhuuu/p/1960118.html