【暴力/网络流】[Codeforces - 739E]Gosha is hunting

题目大意

有n个口袋妖怪,a个精灵球,b个超级球,抓住第i个口袋妖怪的几率分别为 pi ui ,对于每个口袋妖怪,每种球最多只能使用1个,你必须在一开始就决定丢球的方案,使得抓住的口袋妖怪的数量最大。

分析

费用流

对于每个口袋妖怪,如果只扔一个球,那么收益就是 pi 或者 qi ,如果两球都扔,收益就是分别扔两球的和减去 pi×qi ,那么我们建图,建立两个点分别表示两种球,由起点连向它,容量分别为球的数量,费用为0,然后每个点都分别向代表口袋妖怪的点连边,容量为1,费用为 pi qi ,然后每个妖怪向终点连两条边,容量都是1,代价为 pi×qi ,跑一次最大费用最大流即可。

暴力

将所有点分为4类: 0 A B AB
分别对应不扔球,扔一个球和扔两个球的情况和都扔的情况。
将所有球按照 u 降序排序,枚举一个位置i作为 B 最后出现的位置,可以证明,0一定不会在 i 左边出现。

如果存在0,那么我们将在其后面的任意一个超级球扔向它,所得到的答案都会更优。

然后,我们将i左边的元素按照(1p)×u降序排序,枚举一个 j 作为AB最后出现的位置,可以证明 j 的左边一定只能有AB B

如果有A,同样,我们将再其之后在j之前的任意一个超级球扔向它,都会更优

我们枚举i,j,将 j 左边叫做X j i之间叫做 Y ,其余的叫Z
XAB+BY:A+BZ:A+0
X 中的每一个点都使用了超级球,然后就可以计算出Y中超级球的用量,然后就可以计算出 X Z中普通球的用量。
Y 中哪些点使用了超级球呢?
假设Y中所有点都使用的普通球,如果其替换为超级球,贡献就是up,所有就是 Y up大的那些。
X Z中哪些点使用了普通球呢?
X 中使用普通球的贡献为(1u)×p Z 中就是p,排个序就能解决。
那我们用平衡树维护一下,时间复杂度就是 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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值