bzoj 5006(洛谷 4547) [THUWC2017]Bipartite 随机二分图——期望DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5006
https://www.luogu.org/problemnew/show/P4547
算一种可行方案,只要确定出 n 条边即可;概率就是这 n 条边存在的概率,其他边视作无要求,概率贡献都是1;这样的话,一种方案对答案的贡献就是其概率。
考虑把第二组边和第三组边分成概率分别为 1/2 的两条独立的边。对于第二组边再加一条能把4个点都连起来的 1/4 的边,对于第三组边再加一条能把4个点都连起来的 -1/4 的边。
因为算一个方案的概率的时候只看选中的边的概率乘积,所以上述方案可以让概率计算正确。
可以设 dp[ s0 ][ s1 ] 表示左部的点集 s0 和右部的点集 s1 匹配的期望方案数。然后记忆化搜索。(也可以刷表,还会快,但不太会写)
为了避免方案数因为加边顺序而算重,可以规定一个顺序,比如转移到 (s0,s1) 的状态 (d0,d1) 的 d0 一定不含 s0 的 lowbit 之类的。
于是枚举 d0 , d1 ,但会TLE。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define mkp make_pair
#define pii pair<int,int>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=,mod=1e9+;
int n,m,bin[N],dy[(<<)+],iv2,iv4; bool s[N][N];
map<pii,int> s2,mp;
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;}
void upd(int &x){x>=mod?x-=mod:;}
int dfs(int s0,int s1)
{
pii S=mkp(s0,s1);if(mp.count(S))return mp[S];
int lbt=(s0&-s0);
int x=dy[lbt],y,d0=s0^(lbt),p0=s1,d1;
while(p0)//every bit of s1
{
lbt=(p0&-p0);
y=dy[lbt]; d1=s1^lbt; p0^=lbt;
if(s[x][y])
mp[S]=(mp[S]+(ll)iv2*dfs(d0,d1))%mod;
}
p0=d0; x=(s0&-s0);
while(p0)//every bit of (s0-lowbit)
{
lbt=(p0&-p0); d0=(x|lbt); p0^=lbt;//d0:two bits of s0
int p1=s1;//every bit of s1
while(p1)
{
lbt=(p1&-p1); y=lbt; p1^=lbt;
int p2=p1;//d1:every bit of p1
while(p2)
{
lbt=(p2&-p2); d1=(y|lbt); p2^=lbt;
pii d=mkp(d0,d1);
if(s2.count(d))
mp[S]=(mp[S]+(ll)iv4*dfs(s0^d0,s1^d1)*s2[d])%mod+mod,upd(mp[S]);
}
}
}
return mp[S];
}
int main()
{
int m,t,x1,y1,x2,y2;
n=rdn();m=rdn();
bin[]=;dy[]=;for(int i=;i<=n;i++)bin[i]=bin[i-]<<,dy[bin[i]]=i;
iv2=pw(,mod-); iv4=pw(,mod-); mp[mkp(,)]=bin[n];
for(int i=;i<=m;i++)
{
t=rdn();x1=rdn()-;y1=rdn()-;
if(t)x2=rdn()-,y2=rdn()-;
s[x1][y1]=; if(!t)continue;
s[x2][y2]=;
s2[mkp(bin[x1]|bin[x2],bin[y1]|bin[y2])]=(t==?:-);
}
printf("%d\n",dfs(bin[n]-,bin[n]-));
return ;
}
于是改成枚举每条边,并且把两个点集压进一个数而不是两个数里。但还是TLE。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define mkp make_pair
#define pii pair<int,int>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=,mod=1e9+;
int n,m,bin[N],iv2,iv4;
struct Ed{
int x,y,w;
Ed(int x=,int y=,int w=):x(x),y(y),w(w) {}
}ed[M];
map<pii,int> mp;
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;}
void upd(int &x){x>=mod?x-=mod:;}
int dfs(int s0,int s1)
{
pii S=mkp(s0,s1);if(mp.count(S))return mp[S];
int lbt=(s0&-s0);
for(int i=;i<=m;i++)
if((ed[i].x|s0)==s0&&(ed[i].y|s1)==s1&&(ed[i].x&lbt))
mp[S]=(mp[S]+(ll)ed[i].w*dfs(s0^ed[i].x,s1^ed[i].y))%mod;
return mp[S];
}
int main()
{
int tp,t,x1,y1,x2,y2;
n=rdn();tp=rdn();
bin[]=;for(int i=;i<=n;i++)bin[i]=bin[i-]<<;
iv2=pw(,mod-); iv4=pw(,mod-); mp[mkp(,)]=bin[n];
for(int i=;i<=tp;i++)
{
t=rdn();x1=bin[rdn()-];y1=bin[rdn()-];
ed[++m]=Ed(x1,y1,iv2); if(!t)continue;
x2=bin[rdn()-];y2=bin[rdn()-];
ed[++m]=Ed(x2,y2,iv2);
if((x1&x2)||(y1&y2))continue;///
ed[++m]=Ed(x1|x2,y1|y2,t==?iv4:mod-iv4);
}
printf("%d\n",dfs(bin[n]-,bin[n]-));
return ;
}
主要是 map 的大小。在 mp[ s0 ] 里找有没有 s1 比在整个 (s0,s1) 里找有没有 (s0,s1) 快。
并且不要写很多 mp[ s0 ][ s1 ] ,可以用一个临时变量 ret 之类的代替。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=,M=,mod=1e9+;
int n,m,bin[N<<],iv2,iv4,base;
struct Ed{
int s,w;
Ed(int s=,int w=):s(s),w(w) {}
}ed[M];
map<int,int> mp[(<<)+];
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;}
int dfs(int S)
{
int s0=S>>n,s1=S&base;
if(mp[s0].count(s1))return mp[s0][s1];
int ret=;
for(int i=;i<=m;i++)
if((ed[i].s|S)==S&&(ed[i].s<<)>S)
ret=(ret+(ll)ed[i].w*dfs(S^ed[i].s))%mod;
return mp[s0][s1]=ret;
}
int main()
{
int tp,t,s1,s2;
n=rdn();tp=rdn();
bin[]=;for(int i=,j=n<<;i<=j;i++)bin[i]=bin[i-]<<;
iv2=pw(,mod-); iv4=pw(,mod-); base=bin[n]-; mp[][]=bin[n];
for(int i=;i<=tp;i++)
{
t=rdn();s1=bin[rdn()-+n]|bin[rdn()-];
ed[++m]=Ed(s1,iv2); if(!t)continue;
s2=bin[rdn()-+n]|bin[rdn()-];
ed[++m]=Ed(s2,iv2);
if(s1&s2)continue;///
ed[++m]=Ed(s1|s2,t==?iv4:mod-iv4);
}
printf("%d\n",dfs(bin[n<<]-));
return ;
}
bzoj 5006(洛谷 4547) [THUWC2017]Bipartite 随机二分图——期望DP的更多相关文章
- bzoj5006: [THUWC2017 Bipartite]随机二分图
某人在玩一个非常神奇的游戏.这个游戏中有一个左右各 nnn 个点的二分图,图中的边会按照一定的规律随机出现. 为了描述这些规律,某人将这些边分到若干个组中.每条边或者不属于任何组 (这样的边一定不会出 ...
- 洛谷P3239 [HNOI2015]亚瑟王(期望dp)
传送门 stdcall大佬好强 期望的姿势不是很高……据大佬说期望有一个线性性质,也就是说可以把每一张牌的期望伤害算出来然后再加起来就是总的期望伤害 因为每一张牌只能用一次,我们设$dp[i]$表示第 ...
- 洛谷P1365 WJMZBMR打osu! / Easy——期望DP
题目:https://www.luogu.org/problemnew/show/P1365 平方和怎样递推? 其实就是 (x+1)^2 = x^2 + 2*x + 1: 所以我们要关注这里的 x — ...
- 洛谷 P3239 [HNOI2015]亚瑟王(期望+dp)
题面传送门 感觉是道挺好的题,可惜当时没写题解来着的? 根据期望的线性公式,我们求出每个卡牌被发动的概率 \(q_i\),然后 \[ans=\sum\limits_{i=1}^np_id_i \] 于 ...
- 洛谷 P5249 - [LnOI2019]加特林轮盘赌(期望 dp+高斯消元)
题面传送门 期望真 nm 有意思,所以蒟蒻又来颓期望辣 先特判掉 \(P_0=0\) 的情况,下面假设 \(P_0\ne 0\). 首先注意到我们每次将加特林对准一个人,如果这个人被毙掉了,那么相当于 ...
- [BZOJ 3039&洛谷P4147]玉蟾宫 题解(单调栈)
[BZOJ 3039&洛谷P4147]玉蟾宫 Description 有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地. ...
- 【THUWC2017】随机二分图(动态规划)
[THUWC2017]随机二分图(动态规划) 题面 BZOJ 洛谷 题解 如果每天边的限制都是\(0.5\)的概率出现或者不出现的话,可以把边按照二分图左侧的点的编号排序,然后设\(f[i][S]\) ...
- bzoj 3680(洛谷1337) 吊打XXX——模拟退火
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3680 https://www.luogu.org/problemnew/show/P1337 ...
- bzoj 4816: 洛谷 P3704: [SDOI2017]数字表格
洛谷很早以前就写过了,今天交到bzoj发现TLE了. 检查了一下发现自己复杂度是错的. 题目传送门:洛谷P3704. 题意简述: 求 \(\prod_{i=1}^{N}\prod_{j=1}^{M}F ...
随机推荐
- bzoj1627 / P2873 [USACO07DEC]泥水坑Mud Puddles
P2873 [USACO07DEC]泥水坑Mud Puddles bfs入门. 对于坐标为负的情况,我们可以给数组下标加上$abs(min(minx,miny))$转正(根据题意判断) #includ ...
- 20145311实验四 "Android开发基础"
20145311实验四 "Android开发基础" 程序设计过程 实验内容 ·安装Android Studio·运行安卓AVD模拟器·使用安卓运行出虚拟手机并显示HelloWorl ...
- A8逻辑篇1.点亮一个LED(S5PV210.A8)
一.虚拟机安装好后,我们用Fedora 双击.vmx文件,将会在虚拟机中打开 相应的生成: 这些文件 二.进入虚拟机页面 等待启动 账号选择其他 用户名:root 密码:111111 设置页面大小: ...
- Web漏洞挖掘之网络信息探测
我们在搜集目标系统信息的时候主要需要搜集的是:目标服务器系统信息(IP,服务器所用系统等):目标网站子域名:目标网站(服务器)的开放端口:目标域名信息.目标网站内容管理系统(CMS)等. 一.子域名搜 ...
- 51Nod 1503 猪和回文
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1503 思路: 没想到要用DP去解决. 题目是从起点出发走,我们可以从起点 ...
- Redis复制(replication)
介绍 Redis支持简单的主从(master-slave)复制功能,当主Redis服务器更新数据时能将数据同步到从Redis服务器 配置 在Redis中使用复制功能非常容易 在从Redis服务器的re ...
- [原][osgearth]osgearth本地(离线)数据源处理小结
参考:http://docs.osgearth.org/en/latest/data.html Processing Local Source Data If you have geospatial ...
- 递归--练习1--noi3089爬楼梯
递归--练习1--noi3089爬楼梯 一.心得 根据输入,是要写连续输入多个值的程序 二.题目 3089:爬楼梯 总时间限制: 1000ms 内存限制: 65536kB 描述 树老师爬楼梯,他可 ...
- JS学习笔记(模态框JS传参)
博主最近基于django框架的平台第一版差不多完成了 今天整理下开发过程中遇到的前端知识 基于前端bootstrap框架模态框传参问题 上前端html代码: <div class="m ...
- 从数组里随机获取N项
基础知识: 复制数组: (1)循环遍历复制(不推荐) var arry = [1,5,9,7], new_arry = [], n = 0, len = arry.length; for(;n< ...