p4208 [JSOI2008]最小生成树计数

分析

此题难点在于一些最小生成树的性质

可以参考这里

https://www.cnblogs.com/Y-E-T-I/p/8462255.html

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define id(x) wh[sf(x)]
const int mod = 31011;
struct node {
    int x,y,z;
};
node d[1100];
inline bool cmp(const node a,const node b){
    return a.z<b.z;
}
int fa[110],ffa[110],g[110][110],n,m,Ans,wh[110],cnt;
inline int sf(int x){return x==fa[x]?x:fa[x]=sf(fa[x]);}
inline int ssf(int x){return x==ffa[x]?x:ffa[x]=ssf(ffa[x]);}
inline int gs(){
    int i,j,k,ans=1;
    for(i=1;i<cnt;i++)
      for(j=1;j<cnt;j++)
        g[i][j]=(g[i][j]%mod+mod)%mod;
    for(i=1;i<cnt;i++){
      for(j=i;j<cnt;j++)
        if(g[j][i])break;
      if(j>=cnt){
          puts("0");
          exit(0);
      }
      if(j!=i)ans=mod-ans,swap(g[i],g[j]);
      for(j=i+1;j<cnt;j++){
          while(g[j][i]){
            int t=g[i][i]/g[j][i];
            for(k=i;k<cnt;k++)
              g[i][k]=(g[i][k]-1ll*t*g[j][k]%mod+mod)%mod;
            ans=mod-ans;
            swap(g[i],g[j]);
          }
      }
      ans=1ll*ans*g[i][i]%mod;
    }
    return ans;
} 
int main(){
    int i,j,k;
    Ans=1;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
      scanf("%d%d%d",&d[i].x,&d[i].y,&d[i].z);
    sort(d+1,d+m+1,cmp);
    for(i=1;i<=n;i++)fa[i]=i,wh[i]=++cnt;
    for(i=1;i<=m;i++){
      memset(g,0,sizeof(g));
      for(j=1;j<=cnt;j++)ffa[j]=j;
      int _=i,sum=0;
      while(_+1<=m&&d[_+1].z==d[_].z)_++;
      for(j=i;j<=_;j++){
          int x=d[j].x,y=d[j].y;
          if(id(x)==id(y))continue;
          g[id(x)][id(x)]++;
          g[id(y)][id(y)]++;
          g[id(x)][id(y)]--;
          g[id(y)][id(x)]--;
          if(ssf(id(x))!=ssf(id(y)))
          sum++,ffa[ssf(id(x))]=ssf(id(y));
      }
      for(j=1;j<n;j++)
        for(k=j+1;k<=n;k++)
          if(ssf(id(j))!=ssf(id(k))){
              g[id(j)][id(j)]++;
              g[id(k)][id(k)]++;
              g[id(j)][id(k)]--;
              g[id(k)][id(j)]--;
              ffa[ssf(id(j))]=ssf(id(k));
          }
      if(!sum){
          i=_;
          continue;
      }
      Ans=1ll*Ans*gs()%mod;
      cnt=0;
      for(j=i;j<=_;j++){
          int x=d[j].x,y=d[j].y;
          if(sf(x)!=sf(y))fa[sf(x)]=sf(y);
      }
      memset(wh,0,sizeof(wh));
      for(j=1;j<=n;j++)
        if(!id(j))id(j)=++cnt;
      i=_;
      if(cnt==1)break;
    }
    if(cnt>1)puts("0");
      else printf("%d\n",Ans);
    return 0;
}

转载于:https://www.cnblogs.com/yzxverygood/p/11413277.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值