Codeforces53E Dead Ends Matrix_Tree定理 容斥原理

来源:互联网 发布:考研数学辅导书 知乎 编辑:程序博客网 时间:2024/06/06 11:40

题意:给出一个n个点的无向图,求恰好有k个叶子的生成树个数 n<=10

Sol: 

如果是完全图,n个点的带标号生成树个数为n^{n-2} 可以用矩阵树定理或者打表+oeis证明

那么在完全图上求恰好k个叶子的生成树就考虑容斥,钦定有几个点一定是叶子,剩下的点用公式算,每个钦定的叶子都可以接在非钦定的点下面,乘起来在乘个组合数就行,复杂度可以做到O(nlogn) log是快速幂的复杂度

现在给出图的形态了,也可以考虑容斥,枚举钦定叶子的集合,剩下的点生成树个数可以用矩阵树定理算,钦定的点的方案也可以算,记录F(i)表示i集合的方案数

然后容斥就可以对于每个集合枚举包含包含她的集合,加加减减就可以了,枚举集合可以枚举补集的子集再或上现在的集合

总复杂度O(2^n*n^3+3^n)

因为矩阵树定理算行列式要高斯消元,精度问题还是有的,一开始不四舍五入一直过不去

Code:

#include<bits/stdc++.h>#define debug(x) cout<<#x<<"="<<x<<endl#define cls(x) memset(x,0,sizeof x)typedef long long ll;using namespace std;const int maxn = 12;const double eps = 1e-6;int n,m,tar;vector<int> line[maxn];ll f[1<<maxn];int idx[maxn];bool ok[maxn];double a[maxn][maxn];ll ans;inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}int count(int x){int res=0;while(x){if(x&1)res++;x>>=1;}return res;}double gauss(int len){double res=1;for(int i=1;i<=len;i++){int now=i;while(now<=len&&fabs(a[now][i])<=eps) now++;if(now>len) continue;if(now!=i) for(int j=1;j<=len;j++) swap(a[now][j],a[i][j]);double tmp=a[i][i];res*=tmp;for(int j=1;j<=len;j++) a[i][j]/=tmp;for(int j=1;j<=len;j++) if(i!=j){tmp=a[j][i];for(int k=1;k<=len;k++) a[j][k]-=tmp*a[i][k];}}for(int i=1;i<=len;i++) res*=a[i][i];return round(res);}ll calc(int sta){int cnt=count(sta),now=0;if(cnt<tar) return 0;cls(a);cls(ok);for(int i=1;i<=n;i++) if(!(sta&(1<<i>>1))) ok[i]=1,idx[i]=++now;for(int i=1;i<=n;i++) if(ok[i]){int sz=line[i].size();for(int j=0;j<sz;j++){int w=line[i][j];if(ok[w]) a[idx[i]][idx[w]]-=1.0,a[idx[i]][idx[i]]+=1.0;}}ll res=1;for(int i=1;i<=n;i++) if(!ok[i]){int tmp=0,sz=line[i].size();for(int j=0;j<sz;j++) if(ok[line[i][j]]) tmp++;res*=tmp;}return res*gauss(now-1);}int main(){n=read();m=read();tar=read();for(int i=1;i<=m;i++){int x=read(),y=read();line[x].push_back(y);line[y].push_back(x);}int mr=(1<<n)-1;for(int i=1;i<=mr;i++) f[i]=calc(i);for(int i=1;i<=mr;i++){if(count(i)!=tar) continue;int sta=mr-i;for(int s=sta;s;s=(s-1)&sta){if(count(s)&1) ans-=f[i+s];else ans+=f[i+s];}ans+=f[i];}cout<<ans<<endl;return 0;}



原创粉丝点击