容斥原理套矩阵-树。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <utility>
typedef long long ll;
const int MOD = 1e9 + 7, MAXN = 19;
int qpow(int a, int b){
int res = 1;
while(b){
if(b & 1)
res = (ll)res * a % MOD;
a = (ll)a * a % MOD;
b >>= 1;
}
return res;
}
int a[MAXN][MAXN];
int det(int n){
int res = 1;
for(int i = 1; i <= n; ++i){
int r = i;
for(int j = i; j <= n; ++j)
if(a[j][i]){
r = j;
break;
}
if(i ^ r){
std::swap(a[i], a[r]);
res = -res;
}
if(!a[i][i])
return 0;
int inv = qpow(a[i][i], MOD - 2);
for(int j = i + 1; j <= n; ++j){
int d = (ll)a[j][i] * inv % MOD;
for(int k = i; k <= n; ++k)
a[j][k] = (a[j][k] - (ll)a[i][k] * d % MOD) % MOD;
}
res = (ll)res * a[i][i] % MOD;
}
return res;
}
int begin[MAXN], end[MAXN];
std::pair<int, int>edge[MAXN * MAXN * MAXN];
int n;
inline int count(unsigned int status){
int res = 0;
for(int i = 1; i < n; ++i)
if(status & (1 << (i - 1)))
++res;
return res;
}
inline void insert(int u, int v){
++a[u][u], ++a[v][v];
--a[u][v], --a[v][u];
}
int ans;
int main(int argc, char *argv[]){
std::scanf("%d", &n);
for(int c, i = 1; i < n; ++i){
std::scanf("%d", &c);
begin[i] = end[i - 1], end[i] = begin[i] + c;
for(int j = begin[i]; j != end[i]; ++j)
std::scanf("%d%d", &edge[j].first, &edge[j].second);
}
for(int c, s = (1 << (n - 1)) - 1; s; --s){
c = 0;
std::memset(a, 0, sizeof a);
for(int i = 1; i < n; ++i)
if(s & (1 << (i - 1))){
++c;
for(int j = begin[i]; j != end[i]; ++j)
insert(edge[j].first, edge[j].second);
}
ans = ((ll)ans + ((n - c) & 1 ? 1ll : -1ll) * det(n - 1)) % MOD;
}
ans = ((ll)ans + MOD) % MOD;
std::printf("%d\n", ans);
return 0;
}