题目描述
传送门
题目大意:n个点要修n-1条路(形成一棵树)。有n-1个公司,每个公司可以修建某些路径。求每个公司恰好修建一条路的方案数。
题解
生成树计数一般都是用基尔霍夫矩阵求行列式来做,关键是怎么解决每个公司恰好修建一条路的限制。 根据容斥原理答案就是:至少0个公司没有修路的方案-至少1个公司没有修路的方案+至少2个公司没有修路的方案……
代码
using namespace std;
LL a[N][N];
int n,
m,tot,nxt[
10003],point[
10003],u[
10003],v[
10003];
void add(
int x,
int y,
int z)
{
tot++; nxt[tot]=point[
x]; point[
x]=tot; u[tot]=
y; v[tot]=z;
}
LL guass(
int n)
{
for (
int i=
1;i<=n;i++)
for (
int j=
1;j<=n;j++) a[i][j]=(a[i][j]
%p+p)
%p;
int num;
int mark=
0;
for (
int i=
1;i<=n;i++) {
num=i;
for (
int j=i+
1;j<=n;j++)
if (
abs(a[j][i])>
abs(a[num][i]))
num=j;
if (num!=i) mark^=
1;
for (
int j=
1;j<=n;j++) swap(a[i][j],a[num][j]);
for (
int j=i+
1;j<=n;j++)
while (a[j][i]!=
0) {
LL tmp=a[j][i]/a[i][i];
for (
int k=
1;k<=n;k++)
a[j][k]=(a[j][k]+p-tmp
*a[i][k]
%p)
%p;
if (!a[j][i])
break;
mark^=
1;
for (
int k=
1;k<=n;k++) swap(a[j][k],a[i][k]);
}
}
LL ans=
1;
if (mark) ans=-ans;
for (
int i=
1;i<=n;i++)
ans=(ans
*a[i][i])
%p;
ans=(ans
%p+p)
%p;
return ans;
}
int main()
{
freopen(
"a.in",
"r",stdin);
scanf(
"%d",&n);
int n1=n-
1;
for (
int i=
0;i<n1;i++) {
scanf(
"%d",&
m);
int x,
y;
for (
int j=
1;j<=
m;j++) {
scanf(
"%d%d",&
x,&
y);
add(i,
x,
y);
}
}
LL ans=
0;
for (
int i=
0;i<(
1<<n1);i++) {
memset(a,
0,sizeof(a));
int cnt=
0;
for (
int j=
0;j<n1;j++)
if ((i>>j)&
1) {
for (
int k=point[j];k;k=nxt[k]) {
//cout<<u[k]<<
" "<<v[k]<<endl;
a[u[k]][v[k]]--;
a[v[k]][u[k]]--;
a[u[k]][u[k]]++;
a[v[k]][v[k]]++;
}
cnt++;
}
LL t=guass(n1);
cnt=n1-cnt;
if (cnt&
1) ans=(ans-t+p)
%p;
else ans=(ans+t)
%p;
}
printf(
"%lld\n",ans);
}
转载请注明原文地址: https://ju.6miu.com/read-149539.html