【最小生成树】BZOJ1016: [JSOI2008]最小生成树计数
Description
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。
Solution
把所有边权相同的视为边组,每一组边组在最小生成树的条数是固定的,对连通性的贡献也是固定的。(证明可以看http://www.cnblogs.com/Fatedayt/archive/2012/05/10/2494877.html)
在确定贡献之后,爆搜每一组边即可。
用矩阵树也可以做,然而我还不会QwQ。
Code
并查集不能路径压缩,不然就不好回溯时还原了。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=1e3+5,mod=31011; 5 6 struct edge{ 7 int u,v,w; 8 bool operator<(const edge&a) 9 const{return w<a.w;} 10 }e[maxn]; 11 int l[maxn],r[maxn],t[maxn],cnt; 12 int p[maxn]; 13 int find(int x){return p[x]==x?x:find(p[x]);} 14 int n,m; 15 16 int ret; 17 void dfs(int i,int j,int k){ 18 if(j==r[i]+1){ 19 if(k==t[i]) ret++,ret%=mod; 20 return; 21 } 22 int x=find(e[j].u),y=find(e[j].v); 23 if(x!=y){ 24 p[x]=y; 25 dfs(i,j+1,k+1); 26 p[x]=x; 27 } 28 dfs(i,j+1,k); 29 } 30 31 int main(){ 32 scanf("%d%d",&n,&m); 33 for(int i=1;i<=m;i++) 34 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 35 sort(e+1,e+m+1); 36 37 int tot=0; 38 for(int i=1;i<=n;i++) p[i]=i; 39 for(int i=1;i<=m;i++){ 40 if(i==1||e[i].w!=e[i-1].w){ 41 r[cnt]=i-1; 42 l[++cnt]=i; 43 } 44 int x=find(e[i].u),y=find(e[i].v); 45 if(x!=y){ 46 t[cnt]++; 47 tot++; 48 p[x]=y; 49 } 50 } 51 r[cnt]=m; 52 53 54 if(tot!=n-1){ 55 printf("0\n"); 56 return 0; 57 } 58 for(int i=1;i<=n;i++) p[i]=i; 59 60 int ans=1; 61 for(int i=1;i<=cnt;i++){ 62 ret=0; 63 dfs(i,l[i],0); 64 ans=ans*ret,ans%=mod; 65 for(int j=l[i];j<=r[i];j++){ 66 int x=find(e[j].u),y=find(e[j].v); 67 if(x!=y) p[x]=y; 68 } 69 } 70 printf("%d",ans); 71 return 0; 72 }