题意:给出一个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;
}