P4336 [SHOI2016]黑暗前的幻想乡
https://www.luogu.com.cn/problem/P4336
如果没有每个公司分别修一条路的要求,那么可以直接把每个公司能修的路分别加到图中,然后跑矩阵树
加上这个要求,可以看作求有 \(0\) 个公司不修路的方案数,于是容斥,对于每个 \(m \le n-1\) 求出有 \(m\) 个公司不修路的方案数(枚举是哪 \(m\) 个公司),然后根据容斥原理分别乘上 \(1\) 和 \(-1\) 即可
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#define reg register
#define LL_INF (long long)(0x3f3f3f3f3f3f3f3f)
#define INT_INF (int)(0x3f3f3f3f)
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
#define mod 1000000007
#define N 25
int n;
int u[N][N*N],v[N][N*N];
long long a[N][N];
inline long long det(){
long long ans=1;
for(reg int i=1;i<=n;i++){
for(reg int j=i+1;j<=n;j++){
if(a[j][i]>a[i][i]) std::swap(a[j],a[i]),ans=-ans;
while(a[j][i]){
long long k=a[i][i]/a[j][i];
for(reg int h=1;h<=n;h++) a[i][h]=(a[i][h]+(mod-k)*a[j][h])%mod;
std::swap(a[i],a[j]);
ans=-ans;
}
}
}
for(reg int i=1;i<=n;i++) ans=ans*a[i][i]%mod;
return (ans+mod)%mod;
}
int main(){
n=read();
for(reg int i=1;i<n;i++){
u[i][0]=read();
for(reg int j=1;j<=u[i][0];j++) u[i][j]=read(),v[i][j]=read();
}
int lim=1<<(n-1);
long long ans=0;
for(reg int S=0;S<lim;S++){
int cnt=0;
std::memset(a,0,sizeof a);
for(reg int i=1;i<n;i++)if(S&(1<<(i-1))){
cnt++;
for(reg int j=1;j<=u[i][0];j++)
a[u[i][j]][u[i][j]]++,a[v[i][j]][v[i][j]]++,a[u[i][j]][v[i][j]]--,a[v[i][j]][u[i][j]]--;
}
for(reg int i=1;i<=n;i++)for(reg int j=1;j<=n;j++) a[i][j]=(mod+a[i][j])%mod;
cnt=n-cnt;cnt&=1;
n--;
ans=(ans+mod+(cnt?det():(-det())))%mod;
n++;
}
printf("%lld\n",ans);
return 0;
}