求生成树方案的话要用矩阵树定理,然后这个容斥就是常见套路了吧.
code:
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
#define N 18
#define mod 1000000007
#define ll long long
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int a[N][N],n,det[N][N][N],bu[N];
int qpow(int x,int y)
{
int tmp=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) tmp=(ll)tmp*x%mod;
return tmp;
}
int INV(int x) { return qpow(x,mod-2); }
int gauss()
{
int ans=1,i,j,k;
for(i=2;i<=n;++i)
{
k=i;
for(j=i+1;j<=n;++j) if(a[j][i]>a[k][i]) k=j;
if(k!=i) swap(a[i],a[k]),ans*=-1;
if(!a[i][i]) return 0;
int inv=INV(a[i][i]);
for(j=i+1;j<=n;++j)
{
int t=(ll)((ll)inv*a[j][i]%mod+mod)%mod;
for(k=i;k<=n;++k) a[j][k]=(ll)(a[j][k]%mod-(ll)t*a[i][k]%mod+mod)%mod;
}
}
if(ans<0) ans+=mod;
for(i=2;i<=n;++i) ans=(ll)((ll)ans*a[i][i]%mod+mod)%mod;
return ans;
}
// 度数-邻接矩阵
void build(int x)
{
int i,j;
for(i=1;i<=n;++i) for(j=1;j<=n;++j) a[i][j]+=det[x][i][j];
}
int main()
{
// setIO("input");
int i,j,m;
scanf("%d",&n);
for(i=1;i<=n-1;++i)
{
scanf("%d",&m);
for(j=1;j<=m;++j)
{
int x,y;
scanf("%d%d",&x,&y);
det[i][x][x]++;
det[i][y][y]++;
det[i][x][y]--;
det[i][y][x]--;
}
}
for(i=0;i<N;++i) bu[i]=1<<i;
int ans=0;
for(i=bu[n-1]-1;i>0;--i)
{
memset(a,0,sizeof(a));
int siz=0;
for(j=0;bu[j]<=i;++j) if(bu[j]&i) build(j+1),++siz;
int tmp=gauss();
// printf("%d\n",tmp);
int d=((n-1-siz)%2==0)?1:mod-1;
ans=(ll)(ans+(ll)d*tmp%mod+mod)%mod;
}
printf("%d\n",ans);
return 0;
}