zoukankan      html  css  js  c++  java
  • 【BZOJ4709】柠檬(动态规划,单调栈)

    【BZOJ4709】柠檬(动态规划,单调栈)

    题面

    BZOJ

    题解

    从左取和从右取没有区别,本质上就是要分段。
    (f[i])表示前(i)个位置的最大值。
    那么相当于我们枚举一个前面的位置(j),然后找到这一段中最大的(s_0t^2)
    但是这样子很不优秀。
    我们贪心的思考一下,既然这一段最后加起来只能变成某一个(s_0)
    那么,我们这一段开头和结尾都一定要是(s_0)
    否则我们把结尾那些不等于(s_0)的单独分开一段,
    这样子的答案一定不会更差,前缀同理。
    因此每次的(s_0)一定由前面的某个(s_0)转移过来。
    转移是(f[i]=f[j-1]+s[i]t^2),其中(t^2)([j,i])(s[i])的个数。
    发现(t^2)增长很快于(y=x),显然这个式子是具有决策单调性的。
    如果当前位置(klt j),那么一旦(k)的转移优于了(j),那么(k)就永远优于(j)了。(这不显然吗?
    那么,对于每一个(s)都维护一个单调栈((vector))
    每次将后面不优的全部弹出去,然后进行转移。
    注意几点:
    首先是不优的靠计算,记录一下当前位置的前缀(t)的值,然后每次二分检查单调栈里面的第二个元素是否优于栈顶元素,也就是二分查找一下超过的时间。
    还有一种可能出现的情况,即当前第二个元素不比栈顶优秀,但是第三个元素比栈顶优秀。
    对于栈顶的几个元素,假设(a<b<c),如果(a)超过(b)的时间要早于(b)超过(c)的时间,那么(b)是没有意义的。
    所以对于当前位置(i),我们检查栈顶元素和第二个元素超过(i)的时间
    如果第二个元素超过(i)的时间更早,那么第一个元素就没有意义了,可以直接弹掉。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 111111
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    ll f[MAX];
    vector<int> g[MAX];
    int n,a[MAX],num[MAX],s[MAX];
    ll calc(int j,int x){return f[j-1]+1ll*a[j]*x*x;}
    int Time(int x,int y)
    {
    	int l=1,r=n,ret=n+1;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(calc(x,mid-s[x]+1)>=calc(y,mid-s[y]+1))ret=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return ret;
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)
    	{
    		s[i]=++num[a[i]=read()];int l=g[a[i]].size();
    		while(l>=2&&Time(g[a[i]][l-2],g[a[i]][l-1])<=Time(g[a[i]][l-1],i))
    			--l,g[a[i]].pop_back();
    		g[a[i]].push_back(i);++l;
    		while(l>=2&&Time(g[a[i]][l-2],g[a[i]][l-1])<=s[i])
    			--l,g[a[i]].pop_back();
    		f[i]=calc(g[a[i]][l-1],s[i]-s[g[a[i]][l-1]]+1);
    	}
    	printf("%lld
    ",f[n]);
    }
    
    
  • 相关阅读:
    恐怖如斯
    java在vscode中配置环境的坑
    python的迭代器模块
    一个模仿输入print就有这么多知识点
    30个python常用小技巧
    第一个只出现一次的字符
    UIScrollView属性
    iOS 中UISlider常用知识点
    iOS中UISegmentedControl常用属性
    iOS触摸事件
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9307307.html
Copyright © 2011-2022 走看看