P4336 [SHOI2016]黑暗前的幻想乡
首先这题要用到矩阵树,简单来说的话就是一个无向图的生成树个数可以按如下方法计算。设图\(G\)的邻接矩阵为\(A\),度数矩阵为\(D\),其中邻接矩阵的第\(i\)行第\(j\)列表示\(i\)和\(j\)之间有多少条边(允许重边),度数矩阵的第\(i\)行第\(i\)列表示\(i\)的度数且除对角线外所有元素均为\(0\)。那么这个无向图的基尔霍夫矩阵\(K=D-A\)。\(K\)的任意一个主子式(即去掉第\(i\)行第\(i\)列后剩下的矩阵的行列式)的绝对值即为该无向图的生成树
矩阵的行列式的话……直接用高斯消元把它消成上三角矩阵,记录一下行的交换次数表示正负即可
然后本题明显要用到容斥,\(ans=\)刚好有\(n-1\)个公司的方案\(-\)刚好有\(n-2\)个公司的方案\(+...\)
暴力枚举即可
//minamoto
#include<bits/stdc++.h>
#define ll long long
#define rint register int
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
int read(){
int res,f=1;char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=21,P=1e9+7;
inline int add(int x,int y){return x+y>=P?x+y-P:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+P:x-y;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=1ll*a*a%P)if(b&1)res=1ll*res*a%P;return res;}
int n,m,u,v,tot,ans,head[N],a[N][N],G[N][N];struct eg{int nx,u,v;}e[8005];
int det(int n){
int ans=1,f=1,t,inv,mx;
for(rint i=1;i<=n;++i)for(rint j=1;j<=n;++j)G[i][j]=add(a[i][j],P);
for(rint i=1;i<=n;++i){
mx=i;
for(rint j=i;j<=n;++j)if(G[j][i]>G[mx][i])mx=j;
if(mx!=i){for(rint k=i;k<=n;++k)swap(G[i][k],G[mx][k]);f=-f;}
inv=ksm(G[i][i],P-2);
for(rint j=i+1;j<=n;++j)if(G[j][i]){
t=1ll*G[j][i]*inv%P;
for(rint k=i;k<=n;++k)G[j][k]=dec(G[j][k],1ll*G[i][k]*t%P);
}ans=1ll*ans*G[i][i]%P;
}
return add(P,f*ans);
}
void dfs(int x,int y){
// printf("%d\n",ans);
if(x==n)return (void)(ans=y&1?dec(ans,det(n-1)):add(ans,det(n-1)));
dfs(x+1,y);
for(rint i=head[x];i;i=e[i].nx){
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(rint i=head[x];i;i=e[i].nx){
u=e[i].u,v=e[i].v;
++a[u][u],++a[v][v],--a[u][v],--a[v][u];
}
}
inline void add_edge(int i,int u,int v){e[++tot]={head[i],u,v},head[i]=tot;}
int main(){
// freopen("testdata.in","r",stdin);
n=read();
for(rint i=1;i<n;++i){
m=read();
for(rint j=1;j<=m;++j)u=read(),v=read(),add_edge(i,u,v),++a[u][u],++a[v][v],--a[u][v],--a[v][u];
}
dfs(1,0);printf("%d\n",ans);return 0;
}
深深地明白自己的弱小