背景:
h
e
h
e
.
.
.
hehe...
hehe...
题目传送门:
https://www.luogu.org/problemnew/show/P4336
题意:
有
n
n
n个城市,
(
n
−
1
)
(n-1)
(n−1)支工程队,每一支工程队可以修筑一定的道路。现在要修建
(
n
−
1
)
(n-1)
(n−1)条道路连接这些城市,求方案数。
思路:
考虑容斥去重(对于修边集合一样的需要去重)。
a
n
s
=
选
n
个
工
程
队
的
贡
献
−
(
n
−
1
)
个
工
程
队
的
贡
献
+
.
.
.
1
个
工
程
队
的
贡
献
ans=选n个工程队的贡献-(n-1)个工程队的贡献+...1个工程队的贡献
ans=选n个工程队的贡献−(n−1)个工程队的贡献+...1个工程队的贡献(贡献指当前生成树的个数)。
装压枚举工程队合作的所有情况,并连接此时的道路,然后跑基尔霍夫矩阵&矩阵树定理。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod ((LL)(1e9)+7)
using namespace std;
LL D[110][110],A[110][110],K[110][110];
int n;
struct node{int t;int a[110][2];} a[110];
LL dg(LL x,LL k)
{
if(!k) return 1;
LL op=dg(x,k>>1);
if(k&1) return op*op%mod*x%mod; else return op*op%mod;
}
LL inv(LL x,LL y)
{
return x*dg(y,(LL)mod-2)%mod;
}
int get(int x)
{
int op=0;
while(x)
{
op+=(x&1);
x>>=1;
}
return op;
}
void calc(int x,int y)
{
D[x][x]++,D[y][y]++;A[x][y]++,A[y][x]++;
}
LL Matrix_tree(int tot)
{
LL ans=1;
for(int i=1;i<=tot;i++)
{
for(int j=i+1;j<=tot;j++)
while(K[j][i])
{
LL t=inv(K[i][i],K[j][i]);
for(int k=i;k<=tot;k++) K[i][k]=(K[i][k]-K[j][k]*t%mod+mod)%mod;
for(int k=i;k<=tot;k++) swap(K[i][k],K[j][k]);
ans=-ans;
}
ans=(ans*K[i][i]%mod+mod)%mod;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d",&a[i].t);
for(int j=1;j<=a[i].t;j++)
scanf("%d %d",&a[i].a[j][0],&a[i].a[j][1]);
}
LL ans=0;
for(int i=1;i<(1<<(n-1));i++)
{
memset(A,0,sizeof(A));
memset(D,0,sizeof(D));
for(int j=1;j<n;j++)
if(i&(1<<(j-1)))
for(int k=1;k<=a[j].t;k++)
calc(a[j].a[k][0],a[j].a[k][1]);
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
K[j][k]=D[j][k]-A[j][k];
ans=(ans+(((n+get(i))&1)?1:-1)*Matrix_tree(n-1)+mod)%mod;
}
printf("%lld",ans);
}