codeforces 739E - Gosha is hunting
这道题有三种做法,感受一下:
感觉到了歪果仁费尽脑汁想出来的神仙贪心脑洞题被中国人套路算法踩爆的凄凉。。。(我的名字是不是暴露了我的真实实力)
===================================================================================
首先先要明白:有A个A球,B个B球,用了一个A球贡献为ai,B球贡献为bi,两个都用贡献为1-(1-ai)(1-bi)=ai+bi-ai*bi
先讲讲最无脑的费用流吧。。。
显然st先分别流向A球和B球流量为球的个数,费用为0
两种球分别连向所有小精灵,流量为1,费用为贡献
对于小精灵,连向ed分别建一条流量为1费用为0,流量为1费用为-ai*bi的边,这样如果被两种球分别流了,可以减掉多算的
复杂度O(EK)E约等于2n,K是A+B所以大概是O(n^2)带个大常数的,真菜,还是正解有意思
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std; struct node
{
int x,y,c,next;double d;
}a[];int len,last[];
void ins(int x,int y,int c,double d)
{
len++;
a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
a[len].next=last[x];last[x]=len; len++;
a[len].x=y;a[len].y=x;a[len].c=;a[len].d=-d;
a[len].next=last[y];last[y]=len;
} int st,ed;
int pre[],c[]; double d[],ans;
int list[];bool v[];
bool spfa()
{
memset(d,-,sizeof(d));d[st]=;c[st]=(<<);
memset(v,false,sizeof(v));v[st]=true;
int head=,tail=;list[]=st;
while(head!=tail)
{
int x=list[head];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(a[k].c>&&d[y]<d[x]+a[k].d&&(!(d[x]+a[k].d-d[y]<=1e-)))
{
d[y]=d[x]+a[k].d;
c[y]=min(a[k].c,c[x]);
pre[y]=k;
if(v[y]==false)
{
v[y]=true;
list[tail]=y;
tail++;if(tail==)tail=;
}
}
}
v[x]=false;
head++;if(head==)head=;
}
if(fabs(d[ed]-d[])<=1e-)return false;
else
{
int y=ed;ans+=c[ed]*d[ed];
while(y!=st)
{
int k=pre[y];
a[k].c-=c[ed];
a[k^].c+=c[ed];
y=a[k].x;
}
return true;
}
} double A[],B[];
int main()
{
int n,m,aa,bb;
scanf("%d%d%d",&n,&aa,&bb);
for(int i=;i<=n;i++)scanf("%lf",&A[i]);
for(int i=;i<=n;i++)scanf("%lf",&B[i]); len=;st=n+,ed=n+;
ins(st,n+,aa,),ins(st,n+,bb,);
aa=n+,bb=n+;
for(int i=;i<=n;i++)
{
ins(aa,i,,A[i]);
ins(bb,i,,B[i]);
ins(i,ed,,);
ins(i,ed,,-A[i]*B[i]);
} while(spfa());
printf("%.6lf\n",ans); return ;
}
费用流
------------------------------------------------------------------------------
这是瑟瑟发抖的官方钦定贪心。。。
大前提:A球和B球都要完全用完,用得越多期望一定越大
考虑先按B的贡献大到小给小精灵排序,然后枚举最后一个B球用的位置i(这个位置必定不会超过A+B),这样右边的只有可能是不用或用a(然并卵)
这只是构建了一个前提,使得1~i之中必定每个小精灵都用了球,如果中间没有选A球替代B球,B球一定是贪心选择前B个的
我们先假设1~i之中全部都只用了B球,再考虑A球的放法
对于两个球都放而言,对于B球用的个数没有影响,对于贡献的影响为a-a*b
而B球只有B个却放了i个,意味着要用i-B次单独选择A球来替换(注意是刚好i-B次),对于贡献的影响为a-b
考虑对于当前1~i再次排序贪心,如果我们把双选放前面,单A放后面,使得这样的选择方案是最优的,我们用邻项交换的方式做一下:
设二次排序后x<y,x小精灵用两个球,y小精灵用A球,则有ax+bx-ax*bx+ay>ay+by-ay*by+ax 即 bx-ax*bx>by-ay*by
所以我们按照b*(1-a)大到小排序,这样就把两种选择方式分成两段了
然而我们还是不知道断点在哪里,所以我们需要枚举断点j
于是:要在j~i中选择前i-B个贡献最大的(贡献是a-b),以及在1~j-1中(贡献是a-a*b)和i+1~n中(贡献为a)找到A-(i-B)个最大的
这是一个求全局前k大的数的和的问题,可以用数据结构解决
我%的是CQzhangyu(全网唯一写这个的)%到吐于是乎顺便跟着也用了对顶堆其实是复制粘贴splay各种操作拼起来不知道哪里出锅了,你也可以写个平衡树来d飞我啊~~~~
复杂度O(n^2logn)跑得可真快呢像我的常数一样快,真棒!
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const double eps=1e-; struct heap
{
priority_queue<double>a,b;
void del(){while(!b.empty()&&fabs(a.top()-b.top())<=eps)a.pop(),b.pop();}
double top(){del();return a.top();}
void pop(){del();a.pop();}
int size(){return a.size()-b.size();} void push (double x){a.push(x);}
void erase(double x){b.push(x);}
void clear()
{
while(!a.empty())a.pop();
while(!b.empty())b.pop();
}
};
double sum;
struct bst
{
heap A,B; int lim;
void insert(double x)
{
A.push(-x),sum+=x;
if(A.size()>lim)
B.push(-A.top()),sum+=A.top(),A.pop();
}
void del(double x)
{
if(B.size()>&&x<=B.top())B.erase(x);
else
{
sum-=x,A.erase(-x);
if(A.size()<lim&&B.size()>)
A.push(-B.top()),sum+=B.top(),B.pop();
}
}
void clear(){A.clear(),B.clear();}
}H[];
void init(int l1,int l2)
{
H[].clear(),H[].lim=l1;
H[].clear(),H[].lim=l2;
sum=;
} //---------------------------------findkth---------------------------------------------------- struct node{double a,b;}p[];
bool cmp(node n1,node n2){return n1.b>n2.b;}
bool cmd(node n1,node n2){return n1.b*(-n1.a)>n2.b*(-n2.a);}
int main()
{
int n,A,B;
scanf("%d%d%d",&n,&A,&B);
for(int i=;i<=n;i++)scanf("%lf",&p[i].a);
for(int i=;i<=n;i++)scanf("%lf",&p[i].b);
sort(p+,p+n+,cmp); double ss=;for(int i=;i<B;i++)ss+=p[i].b;
double ans=; int li=min(n,A+B);
for(int i=B;i<=li;i++)
{
ss+=p[i].b; sort(p+,p+i+,cmd);
init(i-B,A-(i-B));
for(int j= ;j<=i;j++)H[].insert(p[j].a-p[j].b);
for(int j=i+;j<=n;j++)H[].insert(p[j].a);
ans=max(ans,ss+sum);
for(int j=;H[].lim<=i-j;j++)
{
H[].del(p[j].a-p[j].b);
H[].insert(p[j].a-p[j].a*p[j].b);
ans=max(ans,ss+sum);
}
}
printf("%.6lf\n",ans); return ;
}
真惨
----------------------------------------------------------------------
终于进入到了我做这道题的真正目的:练wqs二分
无脑的dp方程:f[i][j][k]表示枚举到第i个小精灵,用了j个A球k个B球
记得我之前讲了啥:A球和B球都要完全用完,用得越多期望一定越大
前面一句联想到wqs二分套路固定选择k个,后面一句说明它的函数值是单调增的,同时用你聪明(???)的脑子想想就知道它是一个上凸包,因为先选的时候肯定是贪心选最大的(注意到这个要求的是最大值,所以二分的值是要减掉的(以前没见过))
因为有两维,我们可以wqs套wqs
你问DP转移?这还用脑子吗?
复杂度O(nlog^2n)也太无聊了,虽然和正解一样又有log又有^2,可是^2在log后面真丑~
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps=1e-; int n,A,B;
double a[],b[];
double f[],g[],h[];
void DP(double x,double y)
{
f[]=;g[]=;h[]=;
for(int i=;i<=n;i++)
{
f[i]=f[i-],g[i]=g[i-],h[i]=h[i-];
double d2=f[i-]+a[i]-x;
double d3=f[i-]+b[i]-y;
double d4=f[i-]+a[i]+b[i]-a[i]*b[i]-x-y;
if(f[i]<d2&&fabs(d2-f[i])>eps) f[i]=d2, g[i]=g[i-]+, h[i]=h[i-];
if(f[i]<d3&&fabs(d3-f[i])>eps) f[i]=d3, g[i]=g[i-] , h[i]=h[i-]+;
if(f[i]<d4&&fabs(d4-f[i])>eps) f[i]=d4, g[i]=g[i-]+, h[i]=h[i-]+;
}
} void wqs(double x)
{
double l=,r=,y;
while(r-l>eps)
{
double mid=(l+r)/;
DP(x,mid);
if(h[n]<=B)
{
y=mid;
r=mid;
}
else l=mid;
}
DP(x,y);f[n]+=B*y;
} int main()
{
scanf("%d%d%d",&n,&A,&B);
for(int i=;i<=n;i++)scanf("%lf",&a[i]);
for(int i=;i<=n;i++)scanf("%lf",&b[i]); double l=,r=,ans;
while(r-l>eps)
{
double mid=(l+r)/;
wqs(mid);
if(g[n]<=A)
{
ans=f[n]+A*mid;
r=mid;
}
else l=mid;
}
printf("%.6lf\n",ans); return ;
}
wqs二分
codeforces 739E - Gosha is hunting的更多相关文章
- Codeforces.739E.Gosha is hunting(DP 带权二分)
题目链接 \(Description\) 有\(n\)只精灵,两种精灵球(高级和低级),每种球能捕捉到第\(i\)只精灵的概率已知.求用\(A\)个低级球和\(B\)个高级球能捕捉到精灵数的最大期望. ...
- Codeforces 749E Gosha is hunting 二分+DP
很神奇的一题 看完题解不由惊叹 题意:$n$个神奇宝贝 $a$个普通球 $b$个高级球 普通球抓住$i$神奇宝贝的概率为$u[i]$ 高级球为$p[i]$ 一起用为$u[i]+p[i]-u[i]*p[ ...
- CF 739E Gosha is Hunting
有 $n$ 个 Pokemon,你有 $A$ 个一类精灵球,$B$ 个二类精灵球 分别给出每个 Pokemon 被这两类精灵球捕捉的概率 求抓到 Pokemon 的最优期望个数 $n\leq 2000 ...
- 【CF739E】Gosha is hunting 贪心
[CF739E]Gosha is hunting 题意:有n个小精灵,你有a个普通球和b个超级球,用普通球抓住第i只小精灵的概率为$A_i$,用超级球抓住第i只小精灵的概率为$u_i$.你必须一开始就 ...
- 【CF739E】Gosha is hunting(动态规划,凸优化)
[CF739E]Gosha is hunting(动态规划,凸优化) 题面 洛谷 CF 题解 一个\(O(n^3)\)的\(dp\)很容易写出来. 我们设\(f[i][a][b]\)表示前\(i\)个 ...
- HZOJ 赤(CF739E Gosha is hunting)
本来没有打算写题解的,时间有点紧.但是这个wqs二分看了好久才明白还是写点东西吧. 题解就直接粘dg的了: 赤(red) 本题来自codeforces 739E,加大了数据范围. 首先对一只猫不会扔两 ...
- Codeforces739E Gosha is hunting
题意:现在有n个精灵,两种精灵球各m1和m2个,每个精灵单独使用第一种精灵球有pi的概率被捕获,单独使用第二种精灵球有ui的概率被捕获,同时使用有1-(1-pi)*(1-ui)的概率被捕获.一种精灵球 ...
- CF739E Gosha is hunting DP+wqs二分
我是从其他博客里看到这题的,上面说做法是wqs二分套wqs二分?但是我好懒呀,只用了一个wqs二分,于是\(O(nlog^2n)\)→\(O(n^2logn)\) 首先我们有一个\(O(n^3)\)的 ...
- 2019.03.12 codeforces739E. Gosha is hunting(dp凸优化)
传送门 题意:nnn个物品,有aaa个XXX道具和bbb个YYY道具,XXX道具移走第iii个物品概率为pip_ipi,YYY道具移走第iii个道具概率为uiu_iui. 对于每个物品每种道具最多 ...
随机推荐
- OpenSSH高级功能之端口转发(Port Forwarding)
在RedHat提供的系统管理员指南中提到OpenSSH不止是一个安全shell,它还具有X11转发(X11 Forwarding)和端口转发(Port Forwarding)的功能.X11功能一般用于 ...
- 【02】GitHub 工具 Octotree
#推荐一个 GitHub 工具 Octotree Chrome extension 它可以让你在看任何仓库时,获得一个左边的树状图.
- 大数据学习——mapreduce案例join算法
需求: 用mapreduce实现select order.orderid,order.pdtid,pdts.pdt_name,oder.amount from orderjoin pdtson ord ...
- Codeforces Round #386 (Div. 2) A+B+C+D!
A. Compote 水题(数据范围小都是水题),按照比例找最小的就行了,3min水过. int main() { int a,b,c; while(~scanf("%d%d%d" ...
- POJ1061青蛙的约会
Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事 ...
- PHP建立和删除目录
<?php/*linux中的文件权限filedir 用户 组 其它 rwx rwx rwx 读写执行 6 4 6 读写 读 读写 7 7 7 rw_ r__ rw_ r__ _w_ ___ r ...
- 大话数据结构——KMP算法(还存在问题)
http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html /*#include& ...
- vagrant的学习 之 ThinkPHP3.2
vagrant的学习 之 ThinkPHP3.2 (1)在web目录下新建tp32目录: cd /home/www/ mkdir tp32 (2)下载框架 我从ThinkPHP官网下载了ThinkPH ...
- BNUOJ Eeny Meeny Moo
Eeny Meeny Moo Time Limit: 1000ms Memory Limit: 65535KB 大家都有这种经验,当太多的人同时使用互联网的时候 ...
- HDU——1054 Strategic Game
Strategic Game Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...