题目链接:http://codeforces.com/contest/626/problem/G
题解:这题很明显买彩票肯定要买贡献最大的也就是说买p[i]*(num[i]+1)/(num[i]+a[i]+1)-p[i]*num[i]/(num[i]+a[i])的最大值,当然这个最大值时随时改变的所以要用线段树来维护,先不考虑加彩票减彩票,可以先一开始for一遍全部的彩票先买好,然后再加彩票的或者减彩票的时候考虑不买那个改买那个。这个就需要在线段树上维护一个买一张获得贡献最大的点和不买那个减掉的贡献最小的一个,每次修改只要更新这个线段树上的这两个点就行。最后注意一下细节
#include <iostream> #include <cstring> #include <cstdio> #define inf 100000000000000 using namespace std; typedef double db; typedef long long ll; const db lim = 1e-12; const int M = 2e5 + 10; int a[M] , num[M]; db p[M]; struct TnT { int l , r; int fi , se; db win , lose , ans; }T[M << 2]; void push_up(int i) { T[i].ans = T[i << 1].ans + T[(i << 1) | 1].ans; T[i].win = max(T[i << 1].win , T[(i << 1) | 1].win); T[i].lose = min(T[i << 1].lose , T[(i << 1) | 1].lose); if(T[i].win == T[i << 1].win) T[i].fi = T[i << 1].fi; else T[i].fi = T[(i << 1) | 1].fi; if(T[i].lose == T[i << 1].lose) T[i].se = T[i << 1].se; else T[i].se = T[(i << 1) | 1].se; } void getnum(int i , int l) { T[i].ans = p[l] * (1.0 * num[l]) / (1.0 * (num[l] + a[l])); T[i].ans = min(T[i].ans , 1.0 * p[l] / (1.0 * 2)); if(num[l] >= a[l]) T[i].win = 0.0; else T[i].win = p[l] * (1.0 * (num[l] + 1)) / (1.0 * (num[l] + a[l] + 1)) - p[l] * (1.0 * num[l]) / (1.0 * (num[l] + a[l])); if(num[l] == 0) T[i].lose = 1.0 * inf; else if(num[l] > a[l]) T[i].lose = 0.0; else T[i].lose = p[l] * (1.0 * num[l]) / (1.0 * (num[l] + a[l])) - p[l] * (1.0 * (num[l] - 1)) / (1.0 * (num[l] + a[l] - 1)); T[i].fi = l , T[i].se = l; } void build(int l , int r , int i) { int mid = (l + r) >> 1; T[i].l = l , T[i].r = r , T[i].win = 0 , T[i].lose = 0 , T[i].ans = 0; if(l == r) { getnum(i , l); return ; } build(l , mid , i << 1); build(mid + 1 , r , (i << 1) | 1); push_up(i); } void update(int pos , int i) { int mid = (T[i].l + T[i].r) >> 1; if(pos == T[i].l && T[i].r == pos) { getnum(i , pos); return ; } if(mid < pos) update(pos , (i << 1) | 1); else update(pos , i << 1); push_up(i); } int main() { int n , t , q; memset(num , 0 , sizeof(num)); scanf("%d%d%d" , &n , &t , &q); for(int i = 1 ; i <= n ; i++) { scanf("%lf" , &p[i]); } for(int i = 1 ; i <= n ; i++) { scanf("%d" , &a[i]); } build(1 , n , 1); for(int i = 1 ; i <= t ; i++) { int now = T[1].fi; num[now]++; update(now , 1); } while(q--) { int t , r; scanf("%d%d" , &t , &r); if(t == 1) a[r]++; else a[r]--; update(r , 1); while(T[1].win - T[1].lose > lim) { int now1 = T[1].fi , now2 = T[1].se; num[now1]++ , num[now2]--; update(now1 , 1); update(now2 , 1); } printf("%.8lf\n" , T[1].ans); } return 0; }