先写了个垃圾版本。。。加强版先咕着
思路:最小生成树的性质?+无脑搜索or矩阵树定理
提交:4次(反思反思)
题解:
简单版:
首先显然最小生成树相同权值的边的数量是不变的(否则就不是最小生成树);
然后就是相同权值的边组成的连通块的状态是不变的(就是不管你从权值为$w$的边中选出哪几条,只要合法,连通块的状态都是一样的),即不同权值的边是互不干扰的。
简单证明:(from 邓大佬)
$tql$
然后我们就可以先造出一颗最小生成树,记录每种权值出现的次数,然后对于相同权值的边直接搜索用哪几条,用非路径压缩的并查集维护连通性(便于恢复连通块状态),把答案乘起来取模,ok。
代码:
#include<cstdio> #include<iostream> #include<algorithm> #define R register int using namespace std; #define ull unsigned long long #define ll long long #define pause (for(R i=1;i<=10000000000;++i)) #define In freopen("NOIPAK++.in","r",stdin) #define Out freopen("out.out","w",stdout) namespace Fread { static char B[1<<15],*S=B,*D=B; #ifndef JACK #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++) #endif inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; if(ch==EOF) return EOF; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } inline bool isempty(const char& ch) {return (ch<=36||ch>=127);} inline void gs(char* s) { register char ch; while(isempty(ch=getchar())); do *s++=ch; while(!isempty(ch=getchar())); } } using Fread::g; using Fread::gs; namespace Luitaryi { const int N=110,M=1010,mod=31011; int n,m,i,cnt,tot; ll ans=1; int fa[N],l[M],r[M],c[M]; struct edge { int u,v,w; edge() {} edge(int uu,int vv,int ww) {u=uu,v=vv,w=ww;} inline bool operator < (const edge& that) const {return w<that.w;} }e[M]; inline int getf(int x) {return x==fa[x]?x:getf(fa[x]);} inline void dfs(int lst,int s) { if(lst==r[i]+1) {tot+=s==c[i]; return;} R u=e[lst].u,v=e[lst].v; R uf=getf(u),vf=getf(v); if(uf!=vf) fa[uf]=vf,dfs(lst+1,s+1), fa[uf]=uf,fa[vf]=vf; dfs(lst+1,s); } inline void main() { n=g(),m=g(); for(R i=1,u,v,w;i<=m;++i) u=g(),v=g(),w=g(),e[i]=edge(u,v,w); sort(e+1,e+m+1); for(R i=1;i<=n;++i) fa[i]=i; for(i=1;i<=m&&tot<n-1;++i) { if(e[i].w!=e[i-1].w) ++cnt,r[cnt-1]=i-1,l[cnt]=i; R u=e[i].u,v=e[i].v; R uf=getf(u),vf=getf(v); if(uf!=vf) ++tot,++c[cnt],fa[uf]=vf; } --i; while(i<m&&e[i+1].w==e[i].w) ++i; r[cnt]=i; if(tot<n-1) return (void)printf("0 "); for(R i=1;i<=n;++i) fa[i]=i; for(i=1;i<=cnt;++i) { tot=0; dfs(l[i],0); ans=ans*tot%mod; for(R j=l[i];j<=r[i];++j) fa[getf(e[j].u)]=getf(e[j].v); } printf("%lld ",ans); } } signed main() { Luitaryi::main(); }
复杂版:
发现:
1.每种权值的边的数量是固定的。
2.不同的生成树中,某一种权值的边任意加入需要的数量后,形成的联通块状态是一样的。
这样一来,我们可以枚举树边的权值$i$,把权值不是$i$的树边都加入图中后进行缩点;对于权值为$i$ 的原图中的边,在缩点后的图中构造基尔霍夫矩阵,用矩阵树定理求出方案数。
代码先咕着$qwq$
2019.07.17