博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Luogu4547 THUWC2017 随机二分图 概率、状压DP
阅读量:4318 次
发布时间:2019-06-06

本文共 3757 字,大约阅读时间需要 12 分钟。


考虑如果只有$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
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 }

转载于:https://www.cnblogs.com/Itst/p/10052767.html

你可能感兴趣的文章
Jquery EasyUI修改行背景的两种方式
查看>>
嵌入式Linux学习笔记(0)基础命令。——Arvin
查看>>
二分图匹配
查看>>
c++ 模板template
查看>>
CString的成员函数详解
查看>>
Appium Studio 初体验(windows做ios自动化,录制appium脚本)
查看>>
Linux常用命令
查看>>
整体二分&cdq分治 ZOJ 2112 Dynamic Rankings
查看>>
【POJ2976】Dropping tests (01分数规划入门题)
查看>>
通过正则表达式获取url中参数
查看>>
cxx signal信号捕获
查看>>
《Android开发艺术探索》读书笔记——Cha3.2.3改变布局参数实现View的滑动
查看>>
python闭包与装饰器
查看>>
Acegi 源码解释
查看>>
Activity的几种启动跳转方式
查看>>
LCA最近公共祖先Tarjan(离线)
查看>>
牛客练习赛16 E求值
查看>>
matlab rank
查看>>
Asp.net系列--基础篇(三)
查看>>
css基础
查看>>