正题
题目链接:http://noi.ac/problem/2139
题目大意
给出nnn个数字的序列aia_iai。然后选出一个不降子序列最大化子序列的aia_iai和减去没有任何一个数被选中的区间数量。
1≤n≤106,1≤ai≤1081\leq n\leq 10^6,1\leq a_i\leq 10^81≤n≤106,1≤ai≤108
解题思路
嗯,考虑朴素的dpdpdp方程,设fif_ifi表示以iii为末尾的值就有
fi=fj+ai+(i−j−1)(i−j)2f_i=f_j+a_i+\frac{(i-j-1)(i-j)}{2}fi=fj+ai+2(i−j−1)(i−j)
然后展开整理一下都乘二就是
fi=fj+2ai+i2−i+j2+j−2ij(aj≤ai,j<i)f_i=f_j+2a_i+i^2-i+j^2+j-2ij(a_j\leq a_i,j<i)fi=fj+2ai+i2−i+j2+j−2ij(aj≤ai,j<i)
除了aj≤aia_j\leq a_iaj≤ai就是一个标准的斜率优化式子了
然后这个东西其实挺好搞的,因为多一个限制直接上CDQCDQCDQ就好了,但是每次左边要归并排序,这样时间复杂度就是O(nlogn)O(n\log n)O(nlogn)的了
但其实还有更暴力的做法,因为既然一个CDQCDQCDQ能做到,那么找些数据结构之类的也肯定能做到。
对于树状数组上每个节点维护一个凸壳然后暴力查询就好了
时间复杂度O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
const ll N=1e6+10;
ll n,m,a[N],b[N],l[N],r[N],f[N],k[N];
vector<ll >q[N];
ll calc(ll i,ll j)
{return k[i]+2*i*j;}
void Change(ll x,ll i){while(x<=m){while(l[x]<r[x]&&(k[i]-k[q[x][r[x]]])*(q[x][r[x]]-q[x][r[x]-1])>=(k[q[x][r[x]]]-k[q[x][r[x]-1]])*(i-q[x][r[x]]))r[x]--,q[x].pop_back();q[x].push_back(i);r[x]++;x+=lowbit(x);}return;
}
ll Ask(ll x,ll i){ll ans=-1e18;while(x){while(l[x]<r[x]&&calc(q[x][l[x]],i)<calc(q[x][l[x]+1],i))l[x]++;if(l[x]<=r[x])ans=max(ans,calc(q[x][l[x]],i));x-=lowbit(x);}return ans;
}
signed main()
{scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld",&a[i]),b[i]=a[i];sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(ll i=1;i<=m;i++)r[i]=-1;Change(1,0);for(ll i=1;i<=n;i++){ll x=lower_bound(b+1,b+1+m,a[i])-b;f[i]=Ask(x,i)+2*a[i]-i*i+i;k[i]=f[i]-i*i-i;Change(x,i);}ll ans=-1e18;for(ll i=1;i<=n;i++)ans=max(ans,f[i]/2-(n-i+1)*(n-i)/2);printf("%lld\n",ans);return 0;
}