卧薪尝胆,厚积薄发。
SHOI2016 黑暗前的幻想乡
Date: Wed Dec 12 18:59:10 CST 2018 In Category: NoCategory

Description:

一个无向图, $n-1$ 个公司,给出每个公司可以修的路,问有多少种方案一棵生成树中每个公司修一条路。
$1\leqslant n\leqslant 17$

Solution:

根据容斥原理,答案 $=$ 所有都选了的生成树个数 $-1$ 个没选的 $+2$ 个没选的 $-\ldots$ ,每个用矩阵树定理算即可。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 19
struct edges{int u,v;}es[MAXN][MAXN * MAXN / 2];
int m[MAXN];
int cnt(int k)
{
int res = 1;
while(k > 0)res += (k & 1),k = k >> 1;
res = ((n - res) & 1) ? -1 : 1;
return res;
}
typedef long long ll;
ll g[MAXN][MAXN];
#define MOD 1000000007
ll power(ll a,int b)
{
ll res = 1;
while(b > 0)
{
if(b & 1)res = res * a % MOD;
a = a * a % MOD;
b = b >> 1;
}
return res;
}
ll matrixtree(int k)
{
memset(g,0,sizeof(g));
for(int i = 1;i < n;++i)
{
if(k & (1 << (i - 1)))
{
for(int l = 1;l <= m[i];++l)
{
int u = es[i][l].u,v = es[i][l].v;
++g[u][u];++g[v][v];
--g[u][v];--g[v][u];
}
}
}
ll ans = 1;
for(int i = 1;i < n;++i)
{
for(int j = i;j < n;++j)
{
if(g[j][i] != 0)
{
if(j != i)
{
for(int l = 1;l < n;++l)swap(g[i][l],g[j][l]);
ans *= -1;
}
break;
}
}
for(int j = i + 1;j < n;++j)
{
ll ratio = g[j][i] * power(g[i][i],MOD - 2) % MOD;
for(int l = 1;l < n;++l)
{
g[j][l] = (g[j][l] - g[i][l] * ratio % MOD + MOD) % MOD;
}
}
}
ans = (ans + MOD) % MOD;
for(int i = 1;i < n;++i)ans = ans * g[i][i] % MOD;
return ans;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i < n;++i)
{
scanf("%d",&m[i]);
for(int k = 1;k <= m[i];++k)
{
scanf("%d%d",&es[i][k].u,&es[i][k].v);
}
}
int ans = 0;
for(int i = 1;i < (1 << (n - 1));++i)
{
ans = (ans + cnt(i) * matrixtree(i) % MOD + MOD) % MOD;
}
cout << ans << endl;
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡