zoukankan      html  css  js  c++  java
  • 【BZOJ4709】[Jsoi2011]柠檬 斜率优化+单调栈

    【BZOJ4709】[Jsoi2011]柠檬

    Description

    Flute 很喜欢柠檬。它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬。贝壳一共有 N (1 ≤ N ≤ 100,000) 只,按顺序串在树枝上。为了方便,我们从左到右给贝壳编号 1..N。每只贝壳的大小不一定相同,贝壳 i 的大小为 si(1 ≤ si ≤10,000)。变柠檬的魔法要求,Flute 每次从树枝一端取下一小段连续的贝壳,并选择一种贝壳的大小 s0。如果 这一小段贝壳中 大小为 s0 的贝壳有 t 只,那么魔法可以把这一小段贝壳变成 s0t^2 只柠檬。Flute 可以取任意多次贝壳,直到树枝上的贝壳被全部取完。各个小段中,Flute 选择的贝壳大小 s0 可以不同。而最终 Flute 得到的柠檬数,就是所有小段柠檬数的总和。Flute 想知道,它最多能用这一串贝壳变出多少柠檬。请你帮忙解决这个问题。

    Input

    第 1 行:一个整数,表示 N。
    第 2 .. N + 1 行:每行一个整数,第 i + 1 行表示 si。

    Output

    仅一个整数,表示 Flute 最多能得到的柠檬数。

    Sample Input

    5
    2
    2
    5
    2
    3

    Sample Output

    21
    //Flute 先从左端取下 4 只贝壳,它们的大小为 2, 2, 5, 2。选择 s0 = 2,那么这一段里有 3 只大小为 s0 的贝壳,通过魔法可以得到 2×3^2 = 18 只柠檬。再从右端取下最后一只贝壳,通过魔法可以得到 1×3^1 = 3 只柠檬。总共可以得到 18 + 3 = 21 只柠檬。没有比这更优的方案了。

    题解:大爷说他从来没做过用单调栈优化的斜率优化,唯一的一道还是他自己出的,不过今天我也算是见过第一道这样的题了。

    首先,从两边进行操作可以看成只从一边进行操作,然后我们将原序列反过来再做一遍就行了。

    其次,每次施魔法时,区间的左端点和右端点一定都是相同种类的贝壳,这告诉我们应该将每种颜色放到一起处理。然后可以列出DP方程:

    $f[i]=max{f[j-1]+(s[i]-s[j]+1)^2*color}$。

    其中s[i]表示i这个颜色的前缀和,然后移项

    $f[j-1]+(s[j]-1)^2*color=2*s[i]*color*(s[j]-1)+f[i]-s[i]*v[i]$

    发现x单调递增,y单调递增,k也单调递增,求的还是上凸包!所以用对于每个颜色都用一个单调栈维护即可。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #define y(_) (f[(_)-1]+(s[_]-1)*(s[_]-1)*j)
    #define x(_) (s[_]-1)
    #define k (2*s[i]*j)
    using namespace std;
    const int maxn=100010;
    typedef long long ll;
    int n,m;
    ll ans;
    int t[maxn],last[maxn],pre[maxn];
    ll v[maxn],s[maxn],f[maxn],g[maxn];
    vector<int> st[maxn];
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd();
    	int i,j;
    	for(i=1;i<=n;i++)	v[i]=rd(),pre[i]=last[v[i]],last[v[i]]=i,s[i]=s[pre[i]]+1,m=max(m,(int)v[i]);
    	for(i=1;i<=m;i++)	t[i]=-1;
    	for(i=1;i<=n;i++)
    	{
    		j=v[i];
    		while(t[j]>0&&(y(i)-y(st[j][t[j]]))*(x(st[j][t[j]])-x(st[j][t[j]-1]))>=(y(st[j][t[j]])-y(st[j][t[j]-1]))*(x(i)-x(st[j][t[j]])))	t[j]--,st[j].erase(st[j].end()-1);
    		t[j]++,st[j].push_back(i);
    		while(t[j]>0&&(y(st[j][t[j]])-y(st[j][t[j]-1]))<=k*(x(st[j][t[j]])-x(st[j][t[j]-1])))	t[j]--,st[j].erase(st[j].end()-1);
    		g[i]=f[i]=f[st[j][t[j]]-1]+(s[i]-s[st[j][t[j]]]+1)*(s[i]-s[st[j][t[j]]]+1)*j;
    	}
    	for(i=1;(i<<1)<=n;i++)	swap(v[i],v[n-i+1]);
    	memset(last,0,sizeof(last));
    	for(i=1;i<=n;i++)	pre[i]=last[v[i]],last[v[i]]=i,s[i]=s[pre[i]]+1;
    	for(i=1;i<=m;i++)	st[i].clear(),t[i]=-1;
    	for(i=1;i<=n;i++)
    	{
    		j=v[i];
    		while(t[j]>0&&(y(i)-y(st[j][t[j]]))*(x(st[j][t[j]])-x(st[j][t[j]-1]))>=(y(st[j][t[j]])-y(st[j][t[j]-1]))*(x(i)-x(st[j][t[j]])))	t[j]--,st[j].erase(st[j].end()-1);
    		t[j]++,st[j].push_back(i);
    		while(t[j]>0&&(y(st[j][t[j]])-y(st[j][t[j]-1]))<=k*(x(st[j][t[j]])-x(st[j][t[j]-1])))	t[j]--,st[j].erase(st[j].end()-1);
    		f[i]=f[st[j][t[j]]-1]+(s[i]-s[st[j][t[j]]]+1)*(s[i]-s[st[j][t[j]]]+1)*j;
    	}
    	ans=0;
    	for(i=0;i<=n;i++)	ans=max(ans,g[i]+f[n-i]);
    	printf("%lld",ans);
    	return 0;
    }

     

  • 相关阅读:
    4. RDMA操作类型
    3. RDMA基本元素
    2. 比较基于Socket与RDMA的通信
    1. RDMA概述
    win10 如何开启hyper-v虚拟机
    将Oracle 当前日期加一天、一小时、一分钟
    github搜索案例(axios、pubsub、fetch)
    Tolist案例(父子传参实现增删改)
    5.key的使用
    4.React生命周期
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7643848.html
Copyright © 2011-2022 走看看