[BZOJ4709][JSOI2011]柠檬 决策单调性优化dp

题解:

解法1:

单调栈优化

首先发现一个性质就是

如果当前从i转移比从j转移更加优秀

那么之后就不会从j转移

所以我们考虑利用这个性质

我们要维护一个队列保证前一个超过后一个的时间单调不减

怎么来维护呢

我们计算s[t-2]超过s[t-1]的时间t1,s[t-1]超过i的时间t2,如果t1<t2就说明了s[t-1]没有用了

另外再更新的时候我们算一下相邻两个哪个比较有用,要是前面哪个就弹栈

解法2:

f[i]=max(f[j1]+a[j]×(s[i]s[j]+1)^2)

我们先尝试一下一般的斜率优化,会发现是不行的

因为会出现s[i]^2和s[i]两项

我们转化一下这个式子

f[j1]+(s[j]1)2color=2s[i]color(s[j]1)+f[i]s[i]v[i]

把左边看成y,右边(s[j]-1)看成y,2*s[i]*color看成系数,后面的看成b

问题就变成了一条直线切割的b最大

显然凸包维护就可以了

代码:

#include <bits/stdc++.h>
using namespace std;
#define IL inline
#define ll long long
#define rint register int
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
#define me(x) memset(x,0,sizeof(x))
const int INF=1e9;
const int N=2e5+10;
int n;
ll f[N];
vector<int> g[N];
int s[N],a[N],cnt[N];
IL ll calc(int x,int y)
{
  return f[x-1]+1ll*a[x]*y*y;
}
IL int find(int x,int y)
{
  int h=max(s[x],s[y]),t=n;
  while(h<t)
  {
    int mid=(h+t)/2;
    if (calc(x,mid-s[x]+1)>=calc(y,mid-s[y]+1)) t=mid;
    else h=mid+1;
  }
  return(h);
}
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  ios::sync_with_stdio(false);
  cin>>n;
  rep(i,1,n)
  {
    int x;
    cin>>x;
    a[i]=x; s[i]=++cnt[x];
    int k1=g[x].size();
    while (k1>=2&&find(g[x][k1-2],g[x][k1-1])<=find(g[x][k1-1],i))
      g[x].pop_back(),k1--;
    g[x].push_back(i); k1++;
    while (k1>=2&&find(g[x][k1-2],g[x][k1-1])<=s[i]) g[x].pop_back(),k1--;
    f[i]=calc(g[x][k1-1],s[i]-s[g[x][k1-1]]+1);
  }
  cout<<f[n];
  return 0;
}
原文地址:https://www.cnblogs.com/yinwuxiao/p/9393755.html