*游戏

bzoj十一月月赛

有n轮游戏,第i轮游戏有权值w[i](可正可负),如果选择玩第i次游戏,并且这是第k次玩这个游戏,那么会获得$w_i(k^2+ak+b)$点分数。

你需要输出恰好玩1…n轮游戏最多能获得多少分。n<=1e5,a^2>=4b。

首先a^2>=4b显然只是在告诉我们k^2+ak+b非负,因为如果有负的负负得正就很尴尬了。 a^2>=4b好像没啥用

然后我们可以发现最优解是可以通过增量法构造的,就是说对于k个元素的最优解,存在一种k+1个元素的最优解比这个最优解多一个元素。证明大约如下:

image

那么我们可以考虑从k=0开始每次选择一个元素加进去,作为k+1的方案。

假设加完了,那我们把每次加的元素排成一排,设f[k]表示选k个元素的方案价值,g[k]=f[k]-f[k-1],那么g应该单调不上升(由于答案是凸的)。那么我们考虑对于一个新的元素,啥时候应该把它加进去,我们发现我们可以直接追踪这个g,二分到一个位置使得g单调不上升。

考虑这个元素加进去之后会有什么影响。容易发现一个元素插到某个位置之后,前面的g显然没有影响,对于后面的g,这个元素企图篡权夺位失败,每次失败都要后移一位。

那么对于一个后面的g,假设它原来的rank为r,g'=g+(v[r+1]-v[r])*刚插入的w=g+(2r+a+1)*刚插入的w,r'=r+1。splay上打标记维护即可。打标记的时候需要格外小心,因为多个标记合并的时候rank改变是有先后顺序的,详见代码吧。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666
int an=0,ch[SZ][2],fa[SZ],sz[SZ],root,rk[SZ];
ll g[SZ],dgr[SZ],dg[SZ]; int dr[SZ];
void sett(int x,int r,ll gr,ll gg)
{
    if(!x) return;
    g[x]+=gr*rk[x]+gg; rk[x]+=r;
    dgr[x]+=gr; dg[x]+=gg+gr*dr[x]; dr[x]+=r;
}
inline void pd(int x)
{
    sett(ch[x][0],dr[x],dgr[x],dg[x]);
    sett(ch[x][1],dr[x],dgr[x],dg[x]);
    dr[x]=dgr[x]=dg[x]=0;
}
void upd(int x)
{if(!x) return; sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
void rot(int x)
{
    if(!fa[x]) return;
    int y=fa[x],f=fa[y],p=ch[y][1]!=x;
    fa[x]=f; fa[y]=x;
    if(f) ch[f][ch[f][1]==y]=x;
    int g=ch[x][p];
    ch[x][p]=y; ch[y][!p]=g;
    if(g) fa[g]=y;
    upd(y); upd(x); upd(f);
}
int ss[SZ],sn=0;
void splay(int x,int f=0)
{
    for(int y=x;y!=f;y=fa[y]) ss[++sn]=y;
    while(sn) pd(ss[sn--]);
    while(fa[x]!=f)
    {
        int y=fa[x];
        if(fa[y]!=f)
            (ch[fa[y]][1]==y^ch[y][1]==x)
            ?rot(x):rot(y); rot(x);
    }
    if(!f) root=x;
}
int n,a,b,w[SZ]; ll v[SZ];
void ins(int x,int p,int a)
{
    while(1)
    {
        ++sz[x]; pd(x);
        int r=(g[x]>=v[rk[x]]*ll(a));
        if(!ch[x][r])
        {ch[x][r]=p; sz[p]=1; fa[p]=x; return;}
        x=ch[x][r];
    }
}
ll ans[SZ];
void dfs(int x)
{
    if(!x) return;
    pd(x); ans[rk[x]]=g[x];
    dfs(ch[x][0]); dfs(ch[x][1]);
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;++i)
        scanf("%d",w+i),
        v[i]=(i+a)*(ll)i+b;
    sz[1]=rk[1]=root=1; g[1]=w[1]*v[1];
    for(int i=2;i<=n;++i)
    {
        ins(root,i,w[i]); splay(i);
        rk[i]=sz[ch[i][0]]+1;
        g[i]=(ll)v[rk[i]]*w[i];
        sett(ch[i][1],1,w[i]*2,(a+1)*ll(w[i]));
    }
    dfs(root);
    for(int i=2;i<=n;++i) ans[i]+=ans[i-1];
    for(int i=1;i<=n;++i)
        printf("%lld
",ans[i]);
}
原文地址:https://www.cnblogs.com/zzqsblog/p/8022075.html