Description

[JZOJ5646]【NOI2018模拟4.12】染色游戏_DP


[JZOJ5646]【NOI2018模拟4.12】染色游戏_DP_02

Solution

容易看出不考虑a的限制的话这是一个斜率优化的式子

这就变成了一个二维偏序,既要下标又要权值

当然可以用二维数据结构,如果希望将问题降维的话,排序是一个好的选择

这样就有一种O(Nlog2N)的做法,先按a从小到大排序,然后由于这个点只会转移到它后面的点,那么用李超树维护凸包,动态加线段即可

理论上是不能通过的,实际上一批人过掉了这题还跑的贼快。。

接下来讲O(NlogN)的做法

CDQ分治也是好的办法
考虑按权值排序后分治

递归[l,mid],计算[l,mid]对[mid+1,r]的贡献,然后再递归[mid+1,r]

此时我们就可以将它们分别按下标排好序,然后直接做斜率优化DP即可

注意如果直接sort还是O(Nlog2N)的
可以用归并排序先预处理好所有区间按下标排序的结果

复杂度O(NlogN)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 1000005
#define LL long long
using namespace std;
int a[N],w[N],n,c[N],dt[20][N];
LL f[N],g[N],ans,d[N];
bool cmp(int x,int y)
{
return (a[x]<a[y])||(a[x]==a[y]&&x<y);
}
void doit(int l,int r,int t)
{
if(l==r) f[w[l]]=max(f[w[l]],a[w[l]]-(LL)(w[l]-1)*(w[l])/2),g[w[l]]=(LL)2*f[w[l]]-(LL)w[l]*w[l]-w[l],ans=max(ans,f[w[l]]-(LL)(n-w[l])*(n-w[l]+1)/2);
else
{
int mid=(l+r)/2;
doit(l,mid,t+1);
int j=l;
int x=1,y=0;
d[0]=d[1]=0;
fo(i,mid+1,r)
{
int p=dt[t][i];
while(dt[t][j]<p&&j<=mid)
{
LL q=dt[t][j];
while(x<y&&-(g[q]-g[d[y]])*(d[y]-d[y-1])<=-(g[d[y]]-g[d[y-1]])*(q-d[y])) y--;
d[++y]=q;
j++;
}
while(x<y&&-(g[d[x+1]]-g[d[x]])<=(LL)2*p*(d[x+1]-d[x])) x++;
if(x<=y&&x) f[p]=max(f[p],f[d[x]]+a[p]-(LL)(p-d[x]-1)*(p-d[x])/2);
g[p]=(LL)2*f[p]-p*p-p;
ans=max(ans,f[p]-(LL)(n-p)*(n-p+1)/2);
}
doit(mid+1,r,t+1);
}
}
void gsort(int l,int r,int t)
{
if(l==r) dt[t][l]=w[l];
else
{
int mid=(l+r)/2;
gsort(l,mid,t+1),gsort(mid+1,r,t+1);
int x=l,y=mid+1,le=l-1;
while(x<=mid&&y<=r)
{
if(dt[t+1][x]<dt[t+1][y]) dt[t][++le]=dt[t+1][x++];
else dt[t][++le]=dt[t+1][y++];
}
while(x<=mid) dt[t][++le]=dt[t+1][x++];
while(y<=r) dt[t][++le]=dt[t+1][y++];
}
}
int main()
{
cin>>n;
fo(i,1,n) scanf("%d",&a[i]),w[i]=i,f[i]=-1e15;
sort(w+1,w+n+1,cmp);
f[0]=0;
gsort(1,n,0);
ans=-(LL)n*(LL)(n+1)/2;
doit(1,n,1);
printf("%lld\n",ans);
}