[bzoj 4709--Jsoi2011]柠檬

124 篇文章 2 订阅
26 篇文章 0 订阅

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 想知道,它最多能用这一串贝壳变出多少柠檬。请你帮忙解决这个问题。

这道题首先发现最优解分成的每个段首尾肯定是s0,不然就不需要它们当首尾。
那我们就可以列出dp方程, f [ i ] = m i n s [ j + 1 ] = = s [ i ] ( f [ j ] + ( s u m [ i ] − s u m [ j + 1 ] + 1 ) 2 ∗ s [ i ] ( 或 s [ j + 1 ] ) ) f[i]=min_{s[j+1]==s[i]}(f[j]+(sum[i]-sum[j+1]+1)^2*s[i](或s[j+1])) f[i]=mins[j+1]==s[i](f[j]+(sum[i]sum[j+1]+1)2s[i](s[j+1])),其中sum[i]表示 ∑ j = 1 i [ s [ j ] = = s [ i ] ] \sum_{j=1}^i [s[j]==s[i]] j=1i[s[j]==s[i]]。但我们发现这个dp时间复杂度是n^2,那么就要考虑优化。
作为一位熟练的oi选手,我们先把方程化简,为 f [ j ] + s [ j + 1 ] ∗ ( s u m [ j + 1 ] 2 − 2 ∗ s u m [ j + 1 ] ) = 2 ∗ s u m [ i ] ∗ s [ i ] ∗ s u m [ j + 1 ] + f [ i ] − s [ i ] ∗ ( s u m [ i ] 2 + 2 ∗ s u m [ i ] + 1 ) f[j]+s[j+1]*(sum[j+1]^2-2*sum[j+1])=2*sum[i]*s[i]*sum[j+1]+f[i]-s[i]*(sum[i]^2+2*sum[i]+1) f[j]+s[j+1](sum[j+1]22sum[j+1])=2sum[i]s[i]sum[j+1]+f[i]s[i](sum[i]2+2sum[i]+1)。考虑线性规划,把j化为一个点,横坐标为 2 ∗ s u m [ j + 1 ] 2*sum[j+1] 2sum[j+1],纵坐标为 f [ j ] + s [ j + 1 ] ∗ ( s u m [ j + 1 ] 2 − 2 ∗ s u m [ j + 1 ] ) f[j]+s[j+1]*(sum[j+1]^2-2*sum[j+1]) f[j]+s[j+1](sum[j+1]22sum[j+1]),那么最优决策点就是使一条斜率为 s u m [ i ] ∗ s [ i ] sum[i]*s[i] sum[i]s[i]的直线过此点时的截距比其他点的都要大。
那么很显然是要维护一个上凸壳(下凸壳一定不是最优解,可以自己画一下)。在找最大截距时由于sum[i]*s[i]不是递增的,所以需要二分,因为又是很显然发现最优决策点只要满足前一条直线的斜率 &gt; s u m [ i ] ∗ s [ i ] &gt;sum[i]*s[i] >sum[i]s[i],后一条 &lt; s u m [ i ] ∗ s [ i ] &lt;sum[i]*s[i] <sum[i]s[i]就行了,如果没有就取最后的点。然后维护好这个凸壳就好了。
那么这道题就做完了大雾
update:我sb了,s[i]*sum[i]是递增的,因为s[i]是定值。那么就可以不用写二分,直接维护,时间复杂度是O(n)的。这个做法很显然我就不写了吧。好吧其实是因为我懒

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inline void pr1(int x){write(x),putchar(' ');}
inline void pr2(int x){write(x),puts("");}
vector<int>p[10010];
int sum[100010],tot[10010],w[100010];
long long f[100010];
inline int X(int id){return 2*sum[id+1];}
inline long long Y(int id){return f[id]+1LL*w[id+1]*sum[id+1]*(sum[id+1]-2);}
inline int pf(int x){return x*x;}
inline int erfen(int id,int limit)
{
    int l=1,r=p[id].size()-1,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(Y(p[id][mid])-Y(p[id][mid-1])>=1LL*limit*(X(p[id][mid])-X(p[id][mid-1])))ans=mid,l=mid+1;
        else r=mid-1;
    }return p[id][ans];
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n=read();
    for(int i=1;i<=n;i++)w[i]=read(),sum[i]=++tot[w[i]];
    p[w[1]].push_back(0);
    for(int i=1;i<=n;i++)
    {
        int ans=erfen(w[i],sum[i]*w[i]);
        f[i]=f[ans]+1LL*pf(sum[i]-sum[ans+1]+1)*w[i];
        while(p[w[i+1]].size()>1 && (Y(i)-Y(p[w[i+1]][p[w[i+1]].size()-1]))*(X(p[w[i+1]][p[w[i+1]].size()-1])-X(p[w[i+1]][p[w[i+1]].size()-2]))>=(Y(p[w[i+1]][p[w[i+1]].size()-1])-Y(p[w[i+1]][p[w[i+1]].size()-2]))*(X(i)-X(p[w[i+1]][p[w[i+1]].size()-1])))p[w[i+1]].pop_back(); 
        p[w[i+1]].push_back(i);
    }printf("%lld\n",f[n]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值