bzoj4709柠檬

来源:互联网 发布:c语言射击游戏 编辑:程序博客网 时间:2024/06/09 13:57

一开始写了个O(n2)的暴力DP,找了波规律,发现每种数字的最优转移点一定是同种数字(话说这对于强的人来说可能不必找规律吧,看来我还是太菜了)
然后的想法就是平方函数具有下凸性,可以决策单调性优化,每次维护一个单调栈,再暴力弹栈就行了。
写完后发现跟暴力拍不上,单步了一会发现一种情况是第3个元素优于第1个元素优于第2个元素,然后暴力弹栈就挂了。
百度了题解,知道了可以预计算每个元素被下一个超越的时间,并把此作为另一个关键字来弹栈。
突然感觉自己对决策单调性的理解更深了。
突然发现上一篇博客是8天前的?!

#include<cstdio>#include<vector>using namespace std;typedef long long ll;const int N=100005,M=10005;ll f[N];vector<int> q[M];int a[N],i,j,n,x,y,s[N],lst[N];inline ll sqr(int x){return 1ll*x*x;}inline int calc(int x,int y){    static int l,r,m;    l=1,r=n+1;    while(l<r){        m=(l+r)>>1;        if(f[x-1]+sqr(m+1-s[x])*a[x]<f[y-1]+sqr(m+1-s[y])*a[y])l=m+1;            else r=m;    }    return l;}int main(){    scanf("%d",&n);    for(i=1;i<=n;++i){        scanf("%d",a+i);        s[i]=s[lst[a[i]]]+1,lst[a[i]]=i;        while(q[a[i]].size()>1){            x=q[a[i]].size();            if(calc(q[a[i]][x-2],q[a[i]][x-1])>calc(q[a[i]][x-1],i))break;            q[a[i]].pop_back();        }        q[a[i]].push_back(i);        while(q[a[i]].size()>1){            j=q[a[i]].size();            x=s[i]-s[q[a[i]][j-2]]+1;            y=s[i]-s[q[a[i]][j-1]]+1;            if(f[q[a[i]][j-2]-1]+sqr(x)*a[i]<f[q[a[i]].back()-1]+sqr(y)*a[i])break;            q[a[i]].pop_back();        }        f[i]=f[q[a[i]].back()-1]+sqr(s[i]-s[q[a[i]].back()]+1)*a[i];    }    return printf("%lld\n",f[n]),0;}