n−1n−1个公司,每个公司能修一些边,求每条边都让不同的公司来修的生成树的方案数
看到这种”每个……都要……”的题目就往容斥方面想一想
那么ans=ans=所有都选了的生成树个数−1−1个没选的+2+2个没选的−…−…
生成树个数就可以用直接用矩阵树定理求就好了
然后这题模数是一个质数,所以可以直接求逆元
#include
#define fp(i,a,b) for(register int i=a,I=b+1;i
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=20,P=1e9+7;
typedef int arr[N];
typedef long long ll;
struct eg{int nx,u,v;}e[8005];
int n,ce,ans;arr fi,a[N],G[N];
inline int pls(int a,int b){return a+=b,a>=P?a-P:a;}
inline int sub(int a,int b){return a-=b,a<0?a+P:a;}
inline int fpm(int a,int b){int x=1;for(;b;b>>=1,a=(ll)a*a%P)if(b&1)x=(ll)x*a%P;return x;}
inline int det(int n){
int ans=1,f=1,t,inv,mx;
fp(i,1,n)fp(j,1,n)G[i][j]=pls(a[i][j],P);
fp(i,1,n){mx=i;
fp(j,i,n)if(G[j][i]>G[mx][i])mx=j;
if(mx^i){fp(k,i,n)swap(G[i][k],G[mx][k]);f=-f;}
inv=fpm(G[i][i],P-2);
fp(j,i+1,n)if(G[j][i]){
t=(ll)G[j][i]*inv%P;
fp(k,i,n)G[j][k]=sub(G[j][k],(ll)G[i][k]*t%P);
}ans=(ll)ans*G[i][i]%P;
}
return pls(P,f*ans);
}
void dfs(int x,int y){
if(x==n)return y&1?ans=sub(ans,det(n-1)):ans=pls(ans,det(n-1)),void();
dfs(x+1,y);
for(int i=fi[x];i;i=e[i].nx){
int u=e[i].u,v=e[i].v;
--a[u][u],--a[v][v],++a[u][v],++a[v][u];
}
dfs(x+1,y+1);
for(int i=fi[x];i;i=e[i].nx){
int u=e[i].u,v=e[i].v;
++a[u][u],++a[v][v],--a[u][v],--a[v][u];
}
}
inline void add(int i,int u,int v){e[++ce]={fi[i],u,v},fi[i]=ce;}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%d",&n);
fp(i,1,n-1){
int m,u,v;scanf("%d",&m);
fp(j,1,m)scanf("%d%d",&u,&v),add(i,u,v),++a[u][u],++a[v][v],--a[u][v],--a[v][u];
}
dfs(1,0);
printf("%d",ans);
return 0;
}