[BZOJ1049][HAOI2006]数字序列(dp)

题目描述

传送门

题目大意:给出一个数列,要将其改变成单调上升序列,求最少需要改变多少个数,和在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

题解

第一问,把所有的数减去标号然后求最长不下降子序列就行了
第二问,g(i)表示改好前i个的最小代价,若f(j)+1=f(i)则可以转移,求[j,i]区间内的修改代价可以暴力,枚举一个端点然后将左边的都修改成j,右边的都修改成i,这一步具体的证明可以参考http://pan.baidu.com/share/link?uk=2651016602&shareid=1490516411 orz ydc
因为数据随机,所以我们可以根据第一问求出的f进行分层,然后只在相邻的两个层内暴力,这样时间远远到达不了 O(n3)
并且需要注意的是,在序列的前后各插入一个数,这样好写一些

代码

#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define N 35005

int n,LSH,ans1;
int a[N],b[N],lsh[N],f[N],C[N],p[N],lef[N],rig[N];
LL inf,ans2,g[N];

void add(int loc,int val)
{
    for (int i=loc;i<=LSH;i+=i&-i)
        C[i]=max(C[i],val);
}
int query(int loc)
{
    int ans=0;
    for (int i=loc;i>=1;i-=i&-i)
        ans=max(ans,C[i]);
    return ans;
}
int cmp(int a,int b){return f[a]return (x>0)?x:-x;}
LL calc(int l,int r)
{
    LL now=0;
    for (int i=l+1;ifor (int i=l+1;ireturn ans;
}
int main()
{
    scanf("%d",&n);int Min=2147483647,Max=-2147483647;
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&a[i+1]);a[i+1]-=i+1;
        Min=min(Min,a[i+1]);Max=max(Max,a[i+1]);
    }a[1]=Min-1,a[n+2]=Max+1;n+=2;
    for (int i=1;i<=n;++i) lsh[++LSH]=a[i];
    sort(lsh+1,lsh+LSH+1);LSH=unique(lsh+1,lsh+LSH+1)-lsh-1;
    for (int i=1;i<=n;++i) a[i]=lower_bound(lsh+1,lsh+LSH+1,a[i])-lsh;

    f[1]=1;add(a[1],1);p[1]=1;
    ans1=1;
    for (int i=2;i<=n;++i)
    {
        f[i]=query(a[i])+1;
        add(a[i],f[i]);p[i]=i;
    }
    printf("%d\n",n-f[n]);

    memset(g,127,sizeof(g));inf=g[0];
    g[1]=0;
    sort(p+1,p+n+1,cmp);
    for (int i=1;i<=n;++i)
        if (f[p[i]]!=f[p[i-1]]) lef[f[p[i]]]=i,rig[f[p[i-1]]]=i-1;
    rig[f[n]]=n;
    for (int i=1;iint l=lef[i],r=lef[i+1];
        while (l<=rig[i])
        {
            if (f[l]!=inf)
            {
                while (r<=rig[i+1]&&p[l]>=p[r])
                    ++r;
                if (r>rig[i+1]) break;
                for (int j=r;j<=rig[i+1];++j)
                    if (a[p[j]]>=a[p[l]])
                        g[j]=min(g[j],g[l]+calc(p[l],p[j]));
            }
            ++l;
        }
    }
    printf("%lld\n",g[n]);
}

你可能感兴趣的:(题解,dp,省选)