【JZOJ 3083】塔(加强)

来源:互联网 发布:琅琊榜2 知乎 编辑:程序博客网 时间:2024/06/05 02:55

Description

玩完骰子游戏之后,你已经不满足于骰子游戏了,你要玩更高级的游戏。

   今天你瞄准了下述的好玩的游戏:   首先是主角:塔。你有N座塔一列排开。每座塔各自有高度,有可能相等。   这个游戏就不需要地图了。   你每次可以选择相邻的两座塔合并在一起,即这两座塔的高度叠加后变成了同一座塔。然后原本分别与这两座塔相邻的塔变得与这座新的塔相邻。   你的目标是在使用最少的操作次数在游戏的最后获得一列塔,这些塔的高度从左到右形成一个不下降的数列。

 

抽象题意:给出一个序列,把这个序列分块,使得每块的和不下降,每块的代价为每个块内元素个数-1,求最小的代价。

Solution

很显然有两个性质:
性质1:分的块越多,最后的块的大小就越小,
性质2:块的大小越小,分的块就越多,
现要使分的块尽量多,
根据性质1,可以写出DP使:

fi=min(fj(FjAiAj)+ij1)

f为到j为止分的块数,F为以j为结尾的块的和,A为前缀和,
复杂度:O(n2)
发现过不了,
继续优化,
根据性质2,可以发现,一旦找到一个合法的j,就不用继续找下去了,
FjAiAj

移项:
Fj+AjAi

所有可以根据这个,用一个单调队列,(本题的单调队列绝对是一个异类!!!
队头是合法的,从第二个开始不合法,
随着i的增大,第二个迟早会合法的,如果第二个合法了,就把第一个删掉,
复杂度:O(n)

Code

#include<cstdio>#include<cstdlib>#define fo(i,a,b) for(int i=a;i<=b;i++)using namespace std;typedef long long LL;const int N=1000500;int read(int &n){    char ch=' ';int q=0,w=1;    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());    if(ch=='-')w=-1,ch=getchar();    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;}int m,n,ans,M,FAIL;int a[N];LL f[N][2],A[N];int qu[N];int main(){    int l,r;    read(n);    fo(i,1,n)read(a[i]),A[i]=A[i-1]+a[i];    qu[l=r=1]=0;    fo(i,1,n)    {        while(f[qu[l+1]][1]+A[qu[l+1]]<=A[i]&&l<r)l++;        f[i][0]=f[qu[l]][0]+i-qu[l]-1;        f[i][1]=A[i]-A[qu[l]];        while(l<=r&&f[qu[r]][1]+A[qu[r]]>=f[i][1]+A[i])r--;        qu[++r]=i;    }    printf("%d\n",f[n][0]);    return 0;}
0 0
原创粉丝点击