Luogu4547 THUWC2017 随机二分图 概率、状压DP

传送门


考虑如果只有$0$组边要怎么做。因为$N \leq 15$,考虑状压$DP$。设$f_i$表示当前的匹配情况为$i$时的概率($i$中$2^0$到$2^{N-1}$表示左半边的匹配情况,$2^N$到$2^{2N-1}$表示右半边的匹配情况),转移就是随便取一条边将其起终边对应的位置去掉然后乘上$0.5$。

然而会发现这会重复转移,也就是说先选择$a$再选择$b$与先选择$b$再选择$a$在计算中被算作了两种情况,但实际上只能够算作一种。我们考虑固定$DP$的顺序。我们每一次选择$lowbit(i)$对应的点进行转移,这样转移就是左部分的点从小到大连边,转移也就不会重复了。

接着我们考虑$1$组边和$2$组边。首先我们考虑将这两组边中的两条边拆开考虑,这样会有:只选择第一条边参与贡献概率为$50\%$,只选择第二条边参与贡献概率为$50\%$,两条边同时参与贡献概率为$25\%$。发现只有两条边同时参与贡献时的概率是有问题的,所以我们考虑加上一条边。这一条边对应这一组的两条边,对于$1$组边给予其$25\%$的概率,对于$2$组边给予其$-25\%$的概率,这样概率就是对的了。这一条边要在较小的那一个左端点计算的时候进行计算。

上面那个不是很好理解,慢慢思考一下qwq

最后,$2^{30}$的数组是不可能开下的,考虑到有很多冗余状态,使用记忆化搜索就可以通过这道题了。

  1 #include<bits/stdc++.h>
  2 #define ld long double
  3 //This code is written by Itst
  4 using namespace std;
  5 
  6 inline int read(){
  7     int a = 0;
  8     bool f = 0;
  9     char c = getchar();
 10     while(c != EOF && !isdigit(c)){
 11         if(c == '-')
 12             f = 1;
 13         c = getchar();
 14     }
 15     while(c != EOF && isdigit(c)){
 16         a = (a << 3) + (a << 1) + (c ^ '0');
 17         c = getchar();
 18     }
 19     return f ? -a : a;
 20 }
 21 
 22 const int MAXN = 15 * 15;
 23 const int MOD = 1e9 + 7;
 24 struct Edge{
 25     int start , end , belStart , belEnd;
 26     long long p;
 27 }now[MAXN * 3];
 28 int range[17] , num2[270000] , poww2[31] , N , cnt , inv2 , inv4;
 29 map < int , int > dp;
 30 
 31 inline void add(int x , int y , int belx , int bely , int p){
 32     now[++cnt].start = x;
 33     now[cnt].end = y;
 34     now[cnt].belStart = belx;
 35     now[cnt].belEnd = bely;
 36     now[cnt].p = p;
 37 }
 38 
 39 bool operator <(Edge a , Edge b){
 40     return a.start < b.start;
 41 }
 42 
 43 int dfs(int dir){
 44     if(dp.count(dir))
 45         return dp[dir];
 46     if(!dir)
 47         return 1;
 48     int t = num2[dir & -dir] , sum = 0;
 49     for(int i = range[t] ; i < range[t + 1] ; ++i)
 50         if(dir & poww2[now[i].end])
 51             if(now[i].belStart == -1)
 52                 sum = (sum + dfs(dir ^ poww2[t] ^ poww2[now[i].end]) * now[i].p) % MOD;
 53             else
 54                 if((dir & poww2[now[i].belStart]) && (dir & poww2[now[i].belEnd]))
 55                     sum = (sum + dfs(dir ^ poww2[t] ^ poww2[now[i].end] ^ poww2[now[i].belStart] ^ poww2[now[i].belEnd]) * now[i].p) % MOD;
 56     return dp[dir] = sum;
 57 }
 58 
 59 inline int poww(long long a , int b){
 60     int times = 1;
 61     while(b){
 62         if(b & 1)
 63             times = times * a % MOD;
 64         a = a * a % MOD;
 65         b >>= 1;
 66     }
 67     return times;
 68 }
 69 
 70 int main(){
 71 #ifndef ONLINE_JUDGE
 72     freopen("4547.in" , "r" , stdin);
 73     //freopen("4547.out" , "w" , stdout);
 74 #endif
 75     N = read();
 76     for(int i = 0 ; i < N ; ++i)
 77         num2[1 << i] = i;
 78     poww2[0] = 1;
 79     for(int i = 1 ; i < (N << 1) ; ++i)
 80         poww2[i] = poww2[i - 1] << 1;
 81     inv2 = poww(2 , MOD - 2);
 82     inv4 = poww(4 , MOD - 2);
 83     for(int M = read() ; M ; --M){
 84         int t = read() , x = read() - 1 , y = read() + N - 1;
 85         add(x , y , -1 , -1 , inv2);
 86         if(t){
 87             int belx = read() - 1 , bely = read() + N - 1;
 88             add(belx , bely , -1 , -1 , inv2);
 89             if(belx < x){
 90                 swap(x , belx);
 91                 swap(y , bely);
 92             }
 93             if(belx != x && bely != y)
 94                 add(x , y , belx , bely , t == 1 ? inv4 : MOD - inv4);
 95         }
 96     }
 97     sort(now + 1 , now + cnt + 1);
 98     for(int i = 1 ; i <= cnt ; ++i)
 99         if(!range[now[i].start])
100             range[now[i].start] = i;
101     range[N] = cnt + 1;
102     printf("%lld\n" , 1ll * dfs((1 << (N << 1)) - 1) * poww(2 , N) % MOD);
103     return 0;
104 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/10052767.html