zoukankan      html  css  js  c++  java
  • [SHOI2016] 黑暗前的幻想乡

    容斥原理。

    首先看到题意,发现必然需要求一系列图的生成树数量,因此想到先引入矩阵树定理来求解。

    然后考虑如何求得满足限制的方案数。

    直接算需要考虑到之前哪个公司已经修了边,修了哪些边,很难做。

    可以容斥,先求出所有可以形成的生成树的总数。

    此时会发现明显算多了,刚好由 n - 1 个公司建造的生成树是统计上了,但是也统计上了刚好由 n - 2 个公司建造的生成树。

    这时可以枚举到底是哪 n - 2 个公司建造了这个树,建图的时候只加入这n-2个公司的边,对着这个图跑一边矩阵树,然后依次减去这些集合的方案数。

    以此类推,设点集边集形成的图的集合为 S,如下式。

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

    很典型的容斥式子。

    #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
    ", ans);
    
      return 0;
    }
    
  • 相关阅读:
    vmware fusion和mac共享目录
    安卓linker源码阅读01
    sublime text 快捷键
    eclipse使用经验汇总
    递归池:
    ubuntu下adb红米
    蛋疼问题汇总you must restart adb and eclipse
    JNI
    ARM寻址
    了解装饰器
  • 原文地址:https://www.cnblogs.com/nanjoqin/p/11360854.html
Copyright © 2011-2022 走看看