[SHOI2016] 黑暗前的幻想鄉

容斥原理。ios

首先看到題意,發現必然須要求一系列圖的生成樹數量,所以想到先引入矩陣樹定理來求解。spa

而後考慮如何求得知足限制的方案數。code

直接算須要考慮到以前哪一個公司已經修了邊,修了哪些邊,很難作。string

能夠容斥,先求出全部能夠造成的生成樹的總數。it

此時會發現明顯算多了,恰好由 n - 1 個公司建造的生成樹是統計上了,可是也統計上了恰好由 n - 2 個公司建造的生成樹。io

這時能夠枚舉究竟是哪 n - 2 個公司建造了這個樹,建圖的時候只加入這n-2個公司的邊,對着這個圖跑一邊矩陣樹,而後依次減去這些集合的方案數。ast

以此類推,設點集邊集造成的圖的集合爲 S,以下式。class

\(ans = \sum_{T \subseteq S} (-1)^{|T| + 1} * Matrix\_tree(T)\)stream

很典型的容斥式子。原理

#include <cmath>
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define u first
#define v second

const int maxn = 20;
const int mod = 1000000000 + 7;
int n, m[maxn], a[maxn][maxn], ans;

std::vector<pair<int, int> > edge[maxn];

inline int Fast_pow(int x, int p) {
  int res = 1;
  for( ; p; x = 1ll * x * x % mod, p = p >> 1) if( p & 1 ) res = 1ll * x * res % mod;
  return res;
}

inline int Gauss(int len) {
  int res = 1;
  for(int i = 1; i <= len; ++i) for(int j = i + 1; j <= len; ++j) {
    if( a[i][i] == 0 ) return 0;
    if( a[j][i] ) {
      int tmp = 1ll * a[j][i] * Fast_pow(a[i][i], mod - 2) % mod;
      for(int k = i; k <= len; ++k) a[j][k] = (a[j][k] - 1ll * tmp * a[i][k] % mod + mod) % mod;
    }
  }
  for(int i = 1; i <= len; ++i) res = 1ll * res * a[i][i] % mod;
  return res;
}

int main(int argc, char const *argv[])
{
  scanf("%d", &n);
  for(int i = 1; i < n; ++i) {
    scanf("%d", m + i);
    for(int x, y, j = 1; j <= m[i]; ++j) scanf("%d%d", &x, &y), edge[i].push_back(make_pair(x, y));
  }
  for(int f = 0, i = 0; i < (1 << (n - 1)); ++i) {
    memset(a, 0, sizeof a), f = 1;
    for(int j = 1; j < n; ++j) if( i & (1 << (j - 1)) ) {
      for(int k = 0; k < m[j]; ++k) {
        --a[edge[j][k].u][edge[j][k].v], --a[edge[j][k].v][edge[j][k].u];
        ++a[edge[j][k].u][edge[j][k].u], ++a[edge[j][k].v][edge[j][k].v];
      }
    } else f = -f;
    for(int j = 1; j <= n; ++j) for(int k = 1; k <= n; ++k) if( a[j][k] < 0 ) a[j][k] = a[j][k] + mod;
    ans = ((ans + f * Gauss(n - 1)) % mod + mod) % mod;
  }
  printf("%d\n", ans);

  return 0;
}
相關文章
相關標籤/搜索