[矩阵树定理 模板题] BZOJ 1016 [JSOI2008]最小生成树计数 & HDU 4408 Minimum Spanning Tree
2017-02-17 21:30
513 查看
根据Kruscal算法的过程 我们可以得到一些结论
来自 这里
如果 A,B 同为 G 的最小生成树,且 A 的边权从小到大为 w(a1),w(a2),w(a3),⋯w(an),B 的边权从小到大为 w(b1),w(b2),w(b3),⋯w(bn),则有 w(ai)=w(bi)。
如果 A,B 同为 G 的最小生成树,如果 A,B 都从零开始从小到大加边(A 加 A 的边,B 加 B 的边)的话,每种权值加完后图的联通性相同。
如果在最小生成树 A 中权值为 v 的边有 k 条,用任意 k 条权值为 v 的边替换 A 中的权为 v 的边且不产生环的方案都是一棵合法最小生成树。
然后就可以干些奇奇怪怪的事情 按边权分阶段 然后这些边会把某些连通块缩成一个大连通块 这些大连通块之间互不影响 只要对这些大连通块分别做矩阵树定理求余子式 用乘法原理就好了
可能还是这里讲的比较清楚啊
来自 这里
如果 A,B 同为 G 的最小生成树,且 A 的边权从小到大为 w(a1),w(a2),w(a3),⋯w(an),B 的边权从小到大为 w(b1),w(b2),w(b3),⋯w(bn),则有 w(ai)=w(bi)。
如果 A,B 同为 G 的最小生成树,如果 A,B 都从零开始从小到大加边(A 加 A 的边,B 加 B 的边)的话,每种权值加完后图的联通性相同。
如果在最小生成树 A 中权值为 v 的边有 k 条,用任意 k 条权值为 v 的边替换 A 中的权为 v 的边且不产生环的方案都是一棵合法最小生成树。
然后就可以干些奇奇怪怪的事情 按边权分阶段 然后这些边会把某些连通块缩成一个大连通块 这些大连通块之间互不影响 只要对这些大连通块分别做矩阵树定理求余子式 用乘法原理就好了
可能还是这里讲的比较清楚啊
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline void read(int &x){ char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } const int N=105; const int M=1005; const int P=31011; struct Tset{ int fat ; void init(int n){ for (int i=1;i<=n;i++) fat[i]=i; } int Fat(int u){ return u==fat[u]?u:fat[u]=Fat(fat[u]); } }S1,S2; struct edge{ int u,v,w; bool operator < (const edge &B) const{ return w<B.w; } }ed[M]; int n,m,ans=1; int A ; int sta ,top ; int a ; inline int det(int n){ int ret=1; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%P+P)%P; for (int i=1;i<=n;i++){ for (int j=i+1;j<=n;j++) while (a[j][i]){ int t=a[i][i]/a[j][i]; for (int k=i;k<=n;k++) a[i][k]=(a[i][k]+P-a[j][k]*t%P)%P; for (int k=i;k<=n;k++) swap(a[i][k],a[j][k]); ret=P-ret; } if (!a[i][i]) return 0; ret=ret*a[i][i]%P; } return ret; } int main(){ int last=0; freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(n); read(m); if (m<n-1) return printf("0\n"),0; for (int i=1;i<=m;i++) read(ed[i].u),read(ed[i].v),read(ed[i].w); sort(ed+1,ed+m+1); S1.init(n); S2.init(n); for (int i=1;i<=m+1;i++){ if ((i>1 && ed[i].w!=ed[i-1].w) || i==m+1){ for (int j=last;j<i;j++) A[S2.Fat(ed[j].u)][S2.Fat(ed[j].v)]++,A[S2.Fat(ed[j].v)][S2.Fat(ed[j].u)]++; for (int j=1;j<=n;j++) if (S2.Fat(j)==j) sta[S1.Fat(j)][++top[S1.Fat(j)]]=j; for (int j=1;j<=n;j++) if (top[j]>1){ for (int k=1;k<=top[j];k++) a[k][k]=0; for (int k=1;k<=top[j];k++) for (int l=k+1;l<=top[j];l++){ int x=sta[j][k],y=sta[j][l]; a[k][l]=-A[x][y]; a[l][k]=-A[x][y]; a[k][k]+=A[x][y]; a[l][l]+=A[x][y]; } (ans*=det(top[j]-1))%=P; } for (int j=last;j<i;j++) A[S2.Fat(ed[j].u)][S2.Fat(ed[j].v)]--,A[S2.Fat(ed[j].v)][S2.Fat(ed[j].u)]--; for (int j=1;j<=n;j++) S2.fat[j]=S1.Fat(j),top[j]=0; last=i; } int x=S1.Fat(ed[i].u),y=S1.Fat(ed[i].v); if (x==y) continue; S1.fat[x]=y; } int cnt=0; for (int i=1;i<=n;i++) cnt+=S1.Fat(i)==i; printf("%d\n",cnt==1?ans:0); return 0; }
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #define cl(x) memset(x,0,sizeof(x)) using namespace std; typedef long long ll; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline void read(int &x){ char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } const int N=105; const int M=1005; struct Tset{ int fat ; void init(int n){ for (int i=1;i<=n;i++) fat[i]=i; } int Fat(int u){ return u==fat[u]?u:fat[u]=Fat(fat[u]); } }S1,S2; struct edge{ int u,v,w; bool operator < (const edge &B) const{ return w<B.w; } }ed[M]; int n,m,ans,P; int A ; int sta ,top ; int a ; inline int det(int n){ int ret=1; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%P+P)%P; for (int i=1;i<=n;i++){ for (int j=i+1;j<=n;j++) while (a[j][i]){ int t=a[i][i]/a[j][i]; for (int k=i;k<=n;k++) a[i][k]=(a[i][k]+P-(ll)a[j][k]*t%P)%P; for (int k=i;k<=n;k++) swap(a[i][k],a[j][k]); ret=P-ret; } if (!a[i][i]) return 0; ret=(ll)ret*a[i][i]%P; } return ret; } int main(){ int last; freopen("t.in","r",stdin); freopen("t.out","w",stdout); while (1){ read(n); read(m); read(P); if (!n && !m && !P) break; ans=1; last=0; for (int i=1;i<=m;i++) read(ed[i].u),read(ed[i].v),read(ed[i].w); if (m<n-1) { printf("0\n"); continue; } sort(ed+1,ed+m+1); S1.init(n); S2.init(n); for (int i=1;i<=m+1;i++){ if ((i>1 && ed[i].w!=ed[i-1].w) || i==m+1){ for (int j=last;j<i;j++) A[S2.Fat(ed[j].u)][S2.Fat(ed[j].v)]++,A[S2.Fat(ed[j].v)][S2.Fat(ed[j].u)]++; for (int j=1;j<=n;j++) if (S2.Fat(j)==j) sta[S1.Fat(j)][++top[S1.Fat(j)]]=j; for (int j=1;j<=n;j++) if (top[j]>1){ for (int k=1;k<=top[j];k++) a[k][k]=0; for (int k=1;k<=top[j];k++) for (int l=k+1;l<=top[j];l++){ int x=sta[j][k],y=sta[j][l]; a[k][l]=-A[x][y]; a[l][k]=-A[x][y]; a[k][k]+=A[x][y]; a[l][l]+=A[x][y]; } ans=(ll)ans*det(top[j]-1)%P; } for (int j=last;j<i;j++) A[S2.Fat(ed[j].u)][S2.Fat(ed[j].v)]--,A[S2.Fat(ed[j].v)][S2.Fat(ed[j].u)]--; for (int j=1;j<=n;j++) S2.fat[j]=S1.Fat(j),top[j]=0; last=i; } int x=S1.Fat(ed[i].u),y=S1.Fat(ed[i].v); if (x==y) continue; S1.fat[x]=y; } int cnt=0; for (int i=1;i<=n;i++) cnt+=S1.Fat(i)==i; printf("%d\n",cnt==1?ans%P:0); } return 0; }
相关文章推荐
- 【BZOJ1016】【JSOI2008】最小生成树计数 & 【BZOJ1543】生成树计数 (kruskal+matrix_tree定理)
- 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
- 【BZOJ 1016】 1016: [JSOI2008]最小生成树计数 (DFS|矩阵树定理)
- HDU 4408 Minimum Spanning Tree (图的最小生成树计数 Kruskal + Matrix_Tree定理)
- [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)
- 【BZOJ 1016】 [JSOI2008]最小生成树计数(matrix-tree定理做法)
- BZOJ.1016.[JSOI2008]最小生成树计数(Matrix Tree定理 Kruskal)
- BZOJ1016 && JSOI2008] 最小生成树计数
- hdu 4408 Minimum Spanning Tree 最小生成树计数
- [生成树 MatrixTree定理] BZOJ 1016 [JSOI2008]最小生成树计数
- HDU 4408 Minimum Spanning Tree(最小生成树计数)
- 【BZOJ 1016】[JSOI2008]最小生成树计数 基尔霍夫矩阵||暴力
- HDU 4408 Minimum Spanning Tree 最小生成树计数
- HDU 4408 Minimum Spanning Tree(最小生成树计数)
- BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)
- HDU 4408 Minimum Spanning Tree(最小生成树计数)
- BZOJ1016 && JSOI2008] 最小生成树计数
- HDU 4408 Minimum Spanning Tree 最小生成树计数裸题
- 【HDU 4408】Minimum Spanning Tree(最小生成树计数)
- HDU 4408 - Minimum Spanning Tree(最小生成树计数)