容斥练习题
也可以容斥
枚举叶子的集合
S
,算出非叶子的点的导出子图的生成树个数,再乘上每个叶子和这些点的边的数量
这样可以算出
然后就跟一般容斥一样
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef int Mat[20][20];
const int P=1e9+7,N=20;
int n,m,k,cur;
int l[N][N],f[N],a[N],b[N],vis[N],t;
int cc[1<<10|5];
inline int Pow(int x,int y){
int ret=1;
for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
return ret;
}
vector<int> lnk[N];
inline int det(Mat a,int n){
int ret=1,re=0;
for(int i=1;i<n;i++){
int p; for(p=i;!a[p][i] && p<n;p++);
if(p==n) return 0;
if(p^i){ for(int j=1;j<=n;j++) swap(a[p][j],a[i][j]); re^=1; }
for(int j=i+1;j<n;j++){
if(!a[j][i]) continue;
int t=1LL*a[j][i]*Pow(a[i][i],P-2)%P;
for(int k=i;k<=n;k++)
a[j][k]=(a[j][k]-1LL*t*a[i][k])%P;
}
}
for(int i=1;i<n;i++) ret=1LL*ret*a[i][i]%P;
if(re) ret=-ret;
return (ret+P)%P;
}
int C[N][N];
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y),l[x][y]=l[y][x]=1;
}
for(int S=1;S<(1<<n);S++){
cur=t=0; cc[S]=cc[S>>1]+(S&1);
for(int i=1;i<=n;i++)
if(S>>(i-1)&1);else a[++t]=i;
static Mat tmp; memset(tmp,0,sizeof(tmp));
for(int i=1;i<=t;i++)
for(int j=1+i;j<=t;j++)
if(l[a[i]][a[j]]){
tmp[i][j]=P-1; tmp[j][i]=P-1; tmp[i][i]++; tmp[j][j]++;
}
cur=det(tmp,t);
for(int i=1;i<=n;i++)
if(S>>(i-1)&1){
int cnt=0;
for(int j=1;j<=n;j++)
if((S>>(j-1)&1) || !l[i][j]); else cnt++;
cur*=cnt;
}
f[cc[S]]+=cur;
}
C[0][0]=1;
for(int i=1;i<=n;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
for(int i=n;i;i--){
for(int j=i+1;j<=n;j++)
f[i]-=C[j][i]*f[j];
}
printf("%d\n",f[k]);
return 0;
}