和小星星那个题一样,随便上一波容斥矩阵树即可。
矩阵树定理求的是边权乘积之和,所以可以做形如“恰好有k条黑边”的树计数(跑差值即可)等等。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<utility>
#include<vector>
#define fir first
#define sec second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define mod 1000000007
#define inv(x) fast_pow(x,mod-2)
#define N 20
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
int n,g[N][N];
inline int fast_pow(int x,int k)
{
int ans=1;
for(;k;x=(lint)x*x%mod,k>>=1)
if(k&1) ans=(lint)ans*x%mod;
return ans;
}
inline int calc(int n)
{
int det=1,v,t;
// for(int i=1;i<=n;i++,cout ln)
// for(int j=1;j<=n;j++) cout<<g[i][j]<<" ";
rep(i,1,n)
{
v=inv(g[i][i]),det=(lint)det*v%mod;
rep(j,i,n) g[i][j]=(lint)g[i][j]*v%mod;
rep(j,1,n) if((i^j)&&(t=g[j][i]))
rep(k,i,n) g[j][k]=(g[j][k]-(lint)t*g[i][k]%mod+mod)%mod;
}
return inv(det);
}
bool cb[N];vector<pii> e[N];
inline int solve(int S)
{
for(int i=1;i<n;i++) cb[i]=true;
for(int i=0;i<n-1;i++) if((S>>i)&1) cb[i+1]=false;
memset(g,0,sizeof(g));
for(int i=1;i<n;i++) if(cb[i])
for(int j=0,x,y;j<(int)e[i].size();j++)
x=e[i][j].fir,y=e[i][j].sec,g[x][y]--,g[y][x]--,g[x][x]++,g[y][y]++;
rep(i,1,n) rep(j,1,n) (g[i][j]+=mod)%=mod;return calc(n-1);
}
inline int gsgn(int x,int y)
{
int c=0;
for(int i=0;i<n-1;i++)
if((x>>i)&1) c^=1;
return c?(mod-y)%mod:y;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int c;scanf("%d",&c);
while(c--)
{
int x,y;scanf("%d%d",&x,&y);
e[i].push_back(make_pair(x,y));
}
}
int all=(1<<(n-1))-1,ans=0;
rep(i,0,all) (ans+=gsgn(i,solve(i)))%=mod;
return !printf("%d\n",ans);
}