歌名 - 歌手
0:00

    Atcoder Regular Contest 066 F genocide【JZOJ5451】

    题目

    这里写图片描述

    分析

    \(s[i]\)表示a前缀和。
    \(f[i]\)表示做完了1~i的友谊颗粒的最优值(不一定选i),那么转移方程为

    \[f[i]=max\{f[i-1],max\{f[j]-s[i]+s[j]+\dfrac{(i-j)(i-j+1)}{2}\}\}$$,用斜率优化来处理这个。 类似的,设$g_i$表示做完了i~n的友谊颗粒的最优值(不一定选i), 将a翻转,像f一样做一遍,再将g翻转就可以了。 对于询问(p,x),如果我们不选择p,那么答案就是$f[i-1]+g[i+1]$ 如果我们选择了p,我们再设$F[i]$表示,必选i的最优值。 $$F[i]=max\{f[j]+g[k]+\dfrac{(k-j+1)(k-j+2)}{2}\}(j<i<k)\]

    时间复杂度是\(O(N^2)\)
    如何可以更快的求出\(F[i]\)呢,
    分治,假设当前做到\([l,r]\),左端点\([l,mid]\),i和右端点在\([mid+1,r]\)
    \(tmp[i]\)表示做完了左端点~i的友谊颗粒,且必选i的最优值
    我们将\([l-1,mid-1]\)的端点扔进斜率优化的单调栈,扫一遍\([mid+1,r]\)求出tmp,

    \[tmp[i]=max\{f[j]+g[i+1]+\dfrac{(i-j+1)(i-j+2)}{2}\}(j\in{[l-1,mid-1]}<i\in{[mid+1,r]}) \]

    那么\(F[i]\)就是当前区间\(tmp[i]\)的后缀max
    因为只考虑了i在\([mid+1,r]\)的情况,反过来做一遍就可以了。
    那么选择了p的的最优值就是\(F[p]+a[p]-x\)

    #include <iostream>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <map>
    #include <queue>
    #include <stack>
    using namespace std;
    const int maxlongint=2147483647;
    const int mo=1e9+7;
    const int N=1000005;
    #define rev(h) for(int i=1;i<=n/2;i++) swap(h[i],h[n-i+1])
    #define val(h,j,k) 1ll*(h[j]-h[k]+s[j]-s[k]-1ll*j*1.0/2+1ll*k*1.0/2+1ll*j*j*1.0/2-1ll*k*k*1.0/2)*1.0/(j-k)
    int n,m,t[N],top;
    long long a[N],s[N],f[N],g[N],F[N],tmp[N];
    void dg(long long *f,long long *g,int l,int r)
    {
    	if(l==r)
    	{
    		F[l]=max(F[l],f[l-1]+g[l+1]+1-a[l]);
    		return;
    	}
    	int mid=(l+r)>>1;
    	top=0;
    	for(int i=l-1;i<=mid;i++)
    	{
    		for(;top>1 && val(f,i,t[top])>=val(f,t[top],t[top-1]);) top--;
    		t[++top]=i;
    	}
    	for(int i=mid+1;i<=r;i++)
    	{
    		for(;top>1 && val(f,t[top],t[top-1])<=i;) top--;
    		int j=t[top];
    		tmp[i]=f[j]-(s[i]-s[j])+1ll*(i-j)*(i-j+1)/2+g[i+1];
    	}
    	for(int i=r-1;i>=mid+1;i--) tmp[i]=max(tmp[i],tmp[i+1]);
    	for(int i=r;i>=mid+1;i--) F[i]=max(F[i],tmp[i]);
    	dg(f,g,l,mid),dg(f,g,mid+1,r);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),s[i]=s[i-1]+a[i],F[i]=-a[i]+1;
    	t[top=1]=0;
    	for(int i=1;i<=n;i++)
    	{
    		for(;top>1 && val(f,t[top],t[top-1])<=i;) top--;
    		int j=t[top];
    		f[i]=max(f[j]-(s[i]-s[j])+1ll*(i-j)*(i-j+1)/2,f[i-1]);
    		for(;top>1 && val(f,i,t[top])>=val(f,t[top],t[top-1]);) top--;
    		t[++top]=i;
    	}
    	rev(a);
    	t[top=1]=0;
    	for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    	for(int i=1;i<=n;i++)
    	{
    		for(;top>1 && val(g,t[top],t[top-1])<=i;) top--;
    		int j=t[top];
    		g[i]=max(g[j]-(s[i]-s[j])+1ll*(i-j)*(i-j+1)/2,g[i-1]);
    		for(;top>1 && val(g,i,t[top])>=val(g,t[top],t[top-1]);) top--;
    		t[++top]=i;
    	}
    	rev(a);
    	for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    	rev(g);
    	dg(f,g,1,n);
    	rev(a);
    	rev(g);
    	for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    	rev(F);
    	rev(f);
    	dg(g,f,1,n);
    	rev(F);
    	rev(g);
    	rev(f);
    	rev(a);
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++)
    	{
    		long long p,x;
    		scanf("%lld%lld",&p,&x);
    		printf("%lld\n",max(f[p-1]+g[p+1],F[p]+a[p]-x));
    	}
    }
    
    posted @ 2018-05-23 21:48  无尽的蓝黄  阅读(191)  评论(0编辑  收藏  举报