codeforces 626G

题目大意

给你n 个奖池,t 张彩票,q 次修改。
每个奖池的奖金为pi ,原来每个奖池有li 张彩票。
每次修改,可以把一个奖池的彩票数+1或-1。
每次修改后问投奖所能获得的奖金的最大期望,每个奖池投奖的彩票数不能超过总彩票数的一半。

思路

在同一个奖池中,后一张彩票的的贡献一定比前一张的贡献要小,我们可以用线段树维护,一个区间贡献最大的点的位置,以及减小一张彩票所减小的贡献最小的点。那么如果正贡献-减小的贡献>0,那么,就可以转移一张彩票。直到不能满足条件为止。

参考程序

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxn 200005
#define oo 1e15
#define db double
#define lim 1e-12
using namespace std;

int n,m,q;

db p[maxn];

int a[maxn],b[maxn];

struct seg{
    db win,lose,ans;
    int w1,w2;
}t[maxn*4];

seg open(int w){
    seg ret;
    ret.ans=p[w]*a[w]/(a[w]+b[w]);
    ret.ans=min(ret.ans,p[w] / 2);
    if (a[w]>=b[w]) ret.win=0;
    else ret.win=p[w]*(a[w]+1) / (a[w]+b[w]+1) - p[w] * a[w] / (a[w]+b[w]);
    if (a[w]==0) ret.lose=oo;
    else if (a[w]>b[w]) ret.lose=0;
    else ret.lose=p[w]*a[w] / (a[w]+b[w]) - p[w]*(a[w]-1) / (a[w]+b[w]-1);
    ret.w1=ret.w2=w;
    return ret;
}

void update(int v){
    t[v].ans=t[v << 1].ans+t[v << 1 | 1].ans;
    t[v].win=max(t[v << 1].win,t[v << 1 | 1].win);
    if (t[v].win==t[v << 1].win) t[v].w1=t[v << 1].w1; 
    else t[v].w1=t[v << 1 | 1].w1;
    t[v].lose=min(t[v << 1].lose,t[v << 1 | 1].lose);
    if (t[v].lose==t[v << 1].lose) t[v].w2=t[v << 1].w2; 
    else t[v].w2=t[v << 1 | 1].w2;
}

void build(int v,int l,int r){
    if (l==r) {
        t[v]=open(l);
        return;
    }
    int mid=(l+r) >> 1;
    build(v << 1,l,mid);
    build(v << 1 | 1,mid+1,r);
    update(v);
}

void change(int v,int l,int r,int x){
    if (l==r) {
        t[v]=open(l);
        return;
    }
    int mid=(l+r) >> 1;
    if (x<=mid) change(v << 1,l,mid,x);
    else change(v << 1 | 1,mid+1,r,x);
    update(v);
}

int main(){
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,n) scanf("%lf",&p[i]);
    fo(i,1,n) scanf("%d",&b[i]);
    build(1,1,n);
    fo(i,1,m) {
        int now=t[1].w1;
        a[now]++;
        change(1,1,n,now);
    }
    fo(i,1,q) {
        int x,y;
        scanf("%d%d",&x,&y);
        if (x==1) b[y]++;
        else b[y]--;
        change(1,1,n,y);
        while (t[1].win-t[1].lose>lim) {
            int now1=t[1].w1,now2=t[1].w2;
            a[now1]++;
            a[now2]--;
            change(1,1,n,now1);
            change(1,1,n,now2);
        }
        printf("%.10lf\n",t[1].ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章