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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章