题目大意
有n个口袋妖怪,a个精灵球,b个超级球,抓住第i个口袋妖怪的几率分别为 pi 和 ui ,对于每个口袋妖怪,每种球最多只能使用1个,你必须在一开始就决定丢球的方案,使得抓住的口袋妖怪的数量最大。
分析
费用流
对于每个口袋妖怪,如果只扔一个球,那么收益就是 pi 或者 qi ,如果两球都扔,收益就是分别扔两球的和减去 pi×qi ,那么我们建图,建立两个点分别表示两种球,由起点连向它,容量分别为球的数量,费用为0,然后每个点都分别向代表口袋妖怪的点连边,容量为1,费用为 pi 或 qi ,然后每个妖怪向终点连两条边,容量都是1,代价为 pi×qi ,跑一次最大费用最大流即可。
暴力
将所有点分为4类:
0 A B AB
分别对应不扔球,扔一个球和扔两个球的情况和都扔的情况。
将所有球按照
u
降序排序,枚举一个位置
如果存在0,那么我们将在其后面的任意一个超级球扔向它,所得到的答案都会更优。
然后,我们将i左边的元素按照
如果有A,同样,我们将再其之后在j之前的任意一个超级球扔向它,都会更优
我们枚举
X:AB+BY:A+BZ:A+0
X
中的每一个点都使用了超级球,然后就可以计算出
那
Y
中哪些点使用了超级球呢?
假设Y中所有点都使用的普通球,如果其替换为超级球,贡献就是
那
X
和
在
X
中使用普通球的贡献为
那我们用平衡树维护一下,时间复杂度就是
O(n2logn)
代码
费用流
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
typedef double LD;
const int MAXN=2010,MAXM=20010;
const LD inf=1e15,eps=1e-8;
struct edge
{
int adj,flow,next;
LD cost;
}e[MAXM];
int n,st,ed,a,b,tot,head[MAXN],mark[MAXN],vis[MAXN];
double p[MAXN],q[MAXN];
LD dist[MAXN];
queue<int> que;
inline void add_edge(int u,int v,int f,LD c)
{
e[tot].adj=v,e[tot].flow=f,e[tot].cost=c,e[tot].next=head[u];
head[u]=tot++;
e[tot].adj=u,e[tot].flow=0,e[tot].cost=-c,e[tot].next=head[v];
head[v]=tot++;
}
inline bool SPFA()
{
fill(dist,dist+n,-inf);
que.push(st),dist[st]=0,vis[st]=true,mark[st]=-1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=head[u];~i;i=e[i].next)
{
int &v=e[i].adj;
if(e[i].flow&&dist[u]+e[i].cost>dist[v]+eps)
{
dist[v]=dist[u]+e[i].cost,mark[v]=i;
if(!vis[v])que.push(v),vis[v]=true;
}
}
}
return dist[ed]>-inf;
}
inline double solve()
{
LD ret=0;
while(SPFA())
{
int delta=~0u>>1;
for(int i=mark[ed];~i;i=mark[e[i^1].adj])delta=min(delta,e[i].flow);
for(int i=mark[ed];~i;i=mark[e[i^1].adj])
{
ret+=delta*e[i].cost;
e[i].flow-=delta,e[i^1].flow+=delta;
}
}
return ret;
}
inline void init()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
for(int i=1;i<=n;i++)scanf("%lf",&q[i]);
st=0,ed=n+1;
add_edge(st,n+2,a,0);
add_edge(st,n+3,b,0);
for(int i=1;i<=n;i++)
{
add_edge(n+2,i,1,p[i]);
add_edge(n+3,i,1,q[i]);
add_edge(i,ed,1,0);
add_edge(i,ed,1,-p[i]*q[i]);
}
n+=4;
}
int main()
{
init();
printf("%.6lf\n",solve());
return 0;
}
暴力
#include<cstdio>
#include<algorithm>
#include<set>
#include<queue>
#define MAXN 2000
using namespace std;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
double u[MAXN+10],p[MAXN+10],ans;
int n,r[MAXN+10],a,b;
struct cmp1{
bool l()(int x,int y)const{
return (1-p[x])*u[x]>(1-p[y])*u[y];
}
};
multiset<int,cmp1>s1;
inline bool cmp(int x,int y){
return u[x]>u[y];
}
void read(){
Read(n),Read(a),Read(b);
int i;
for(i=1;i<=n;i++)
scanf("%lf",&p[i]);
for(i=1;i<=n;i++){
scanf("%lf",&u[i]);
r[i]=i;
}
}
struct node{
double sum,val;
int pri,size;
node *ch[2];
}tree[MAXN*4+10],*tcnt=tree,*root[4];
inline double Get_sum(node *p){
return p?p->sum:0;
}
inline int Get_size(node *p){
return p?p->size:0;
}
inline void update(node *p){
p->sum=Get_sum(p->ch[0])+Get_sum(p->ch[1])+p->val;
p->size=Get_size(p->ch[0])+Get_size(p->ch[1])+1;
}
inline void Rotate(node *&x,bool d){
node *y=x->ch[!d];
x->ch[!d]=y->ch[d];
y->ch[d]=x;
update(x);
x=y;
}
inline void init(node *p){
p->sum=p->val=p->pri=p->size=0;
p->ch[0]=p->ch[1]=0;
}
void insert(node *&p,double val){
if(!p){
init(p=++tcnt);
p->pri=(rand()<<15)+rand();
p->val=p->sum=val;
p->size=1;
return;
}
bool d=val<=p->val;
insert(p->ch[d],val);
if(p->ch[d]->pri>p->pri)
Rotate(p,!d);
update(p);
}
inline double get_sum(node *p,int k){
if(!p)
return 0;
if(k<=Get_size(p->ch[0]))
return get_sum(p->ch[0],k);
else if(k<=Get_size(p->ch[0])+1)
return Get_sum(p->ch[0])+p->val;
else
return Get_sum(p->ch[0])+p->val+get_sum(p->ch[1],k-Get_size(p->ch[0])-1);
}
void erase(node *&p,double val){
if(p->val==val){
if(!p->ch[0]){
p=p->ch[1];
return;
}
else if(!p->ch[1]){
p=p->ch[0];
return;
}
bool d=p->ch[0]->pri>p->ch[1]->pri;
Rotate(p,d);
erase(p->ch[d],val);
update(p);
return;
}
erase(p->ch[val<=p->val],val);
update(p);
}
void solve(){
int i,na,nb,j;
sort(r+1,r+n+1,cmp);
double sum=0,sumu=0,sump=0;
for(i=1;i<=n;i++)
insert(root[0],p[i]);
ans=max(ans,get_sum(root[0],a));
root[0]=0,tcnt=tree;
for(i=1;i<=n;i++){
tcnt=tree,root[0]=root[1]=0;
s1.insert(r[i]);
sump+=p[r[i]];
sumu=0,sum=sump;
// tcnt=0;
for(j=i+1;j<=n;j++)
insert(root[0],p[r[j]]);
for(j=i;j;j--)
insert(root[1],u[r[j]]-p[r[j]]);
if(i-b>a)
break;
ans=max(sump+get_sum(root[1],b)+get_sum(root[0],a-max((i-b),0)),ans);
nb=b,na=a-max((i-b),0);
for(auto j:s1){
sumu+=u[j];
sum-=p[j];
nb--;
if(nb<0)
break;
erase(root[1],u[j]-p[j]);
insert(root[0],(1-u[j])*p[j]);
ans=max(ans,sumu+sum+get_sum(root[1],nb)+get_sum(root[0],na));
}
}
}
int main()
{
srand(2016120194);
read();
solve();
printf("%f\n",ans);
}