1. 程式人生 > >Luogu4547 THUWC2017 隨機二分圖 概率、狀壓DP

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 }