zoukankan      html  css  js  c++  java
  • 【THUWC 2017】随机二分图

    如果只有 $ t=0 $ 的边,可以直接把边关于左边的点排序, $ dp[S] $ 表示左边匹配了前 $ |S| $ 个点,右边匹配集合为 $ S $ 的方案数,这样是40分。

    正解是考虑把边拆分成独立的若干边。

    根据期望的线性性,我们只需要对于每一种匹配方案,求出其能成功的概率。如果我们能够建一张新图,使得对于任意匹配方案,其成功的概率不变,并且所有边相互独立,就可以用40分的算法进行DP.

    当 $ t = 1 $ 时,考虑如果直接把两条边当做相互独立的。如果在一个匹配中,用到了其中的一条边,能满足的概率是 $ frac{1}{2} $ , 没有问题;但是如果一个匹配中用到了其中的两条边,我们算出来它满足的概率是 $ frac{1}{4} $ ,但实际上概率应该是 $ frac{1}{2} $ ,所以我们补一条连接这四个点,出现概率为 $ frac{1}{4} $ 的边,这三条相互独立的边就等价于原来的那组边。

    $ t = 2 $ 时也同理,只是加上去的那条边概率应该是 $ -frac{1}{4} $

    这样一来 $ DP $ 的状态就不一定能满足左边匹配的一定是前 $ |S| $ 个点,但是我们转移的时候可以搞一个记忆化搜索,还是以左边的点为标准将边排序,实测跑得很快。

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define LL long long
    #define pii pair<int, int>
    using namespace std;
    const int N = 1e5 + 10;
    const int mod = 1e9 + 7;
    const int inv2 = (mod + 1) / 2;
    const int inv4 = (mod + 1) / 4;
    
    template<typename T> void read(T &x) {
       char c = getchar(); int f = 0;
       while (c < '0' || c > '9') f |= (c == '0'), c = getchar();
       for (x = 0; c >= '0' && c <= '9'; c = getchar())
          x = (x << 3) + (x << 1) + (c ^ '0');
       if (f) x = -x;
    }
    
    int n, m, E;
    pii e[N];
    map<int, int> dp;
    
    void upd(int &x, int y) {
       (x += y) >= mod ? x -= mod : 0;
    }
    
    int Qpow(int x, int p) {
       int ans = 1;
       while (p) {
          if (p & 1) ans = 1LL * ans * x % mod;
          x = 1LL * x * x % mod;
          p >>= 1;
       }
       return ans;
    }
    
    int dfs(int x) {
       if (dp.count(x)) return dp[x];
       int ans = 0, low = 0;
       for (int i = n - 1; i >= 0; --i) {
          if (~x >> i & 1) low = i;
       }
       for (int i = 1; i <= E; ++i) {
          if ((~x & e[i].fi) == e[i].fi && (e[i].fi >> low & 1)) {
             upd(ans, 1LL * dfs(x ^ e[i].fi) * e[i].se % mod);
          }
       }
       return dp[x] = ans;
    }
    
    int main() {
       read(n); read(m);
       for (int i = 1; i <= m; ++i) {
          int op, a1, b1, a2, b2;
          read(op);
          read(a1); read(b1); b1 += n;
          --a1; --b1;
          if (op == 0) {
             e[++E] = mp((1 << a1) + (1 << b1), inv2);
          }
          else {
             read(a2); read(b2); b2 += n;
             --a2; --b2;
             e[++E] = mp((1 << a1) + (1 << b1), inv2);
             e[++E] = mp((1 << a2) + (1 << b2), inv2);
             if (a1 == a2 || b1 == b2) continue;
             if (op == 1) {
                ++E;
                e[E] = mp(e[E - 1].fi + e[E - 2].fi, inv4);
             }
             if (op == 2) {
                ++E;
                e[E] = mp(e[E - 1].fi + e[E - 2].fi, -inv4 + mod);
             }
          }
       }
       dp[(1 << (n * 2)) - 1] = 1;
       cout << 1LL * dfs(0) * Qpow(2, n) % mod << endl;
       // for (int i = 0; i < (1 << (n * 2)); ++i)
       //    cout << i << ' ' << dp[i] << endl;
       return 0;
    }
    
  • 相关阅读:
    生产者消费者模式 (在已有线程下不断生产不断消费)
    Spring boot Spring cloud 框架搭建
    个人见解
    加入博客园!
    sqlserver 工具
    slqserver 拆分函数
    sqlserver 自定义函数与存储过程的区别
    sqlserver row_number() over() 理解
    C# Newtonsoft.Json JObject移除属性
    PropertyInfo 简单用法
  • 原文地址:https://www.cnblogs.com/Vexoben/p/11728769.html
Copyright © 2011-2022 走看看