现在的位置: 首页 > 综合 > 正文

BZOJ 1049: [HAOI2006]数字序列

2018年04月24日 ⁄ 综合 ⁄ 共 1763字 ⁄ 字号 评论关闭

Description

现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。

Input

第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。

Output

第一行一个整数表示最少需要改变多少个数。 第二行一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

Sample Input

4

5 2 3 5

Sample Output

1

4

HINT

【数据范围】

90%的数据n<=6000。

100%的数据n<=35000。

保证所有数列是随机的。

题解

之前忘写上来了。第一问很简单,不讲了。

对于第二问,我们大概可以猜到,不在子序列中的数只要改到跟某个数相同即可。所以我们要找的每对f[i]和f[i-1]。因为数据是随机的,所以可以认为两者之间距离很小,只要枚举中间的端点即可。

注意:因为在枚举f[i]中,第i个数是保证不改的,所以第n个数不管怎样都不会被改。这显然是错的,所以我们要在第一问前就在序列尾加一个无穷大的元素。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int n,a[35002],f[35002],s[35002],top;
int last[35002],zz;
struct lian{int to,pre;} e[35002];
ll g[35002],sm[35002],sum[35002];
void init()
{
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;i++)
       {scanf("%d",&a[i]);
        a[i]-=i;
       }
}
int erf(int x)
{
    int l=0,r=top,mid,p;
    while(l<=r)
       {mid=(l+r)>>1;
        if(s[mid]<=a[x]) {p=mid; l=mid+1;}
        else r=mid-1;
       }
    return p;
}
void dp()
{
    int i,j,p,ans=1;
    a[++n]=1<<30; f[1]=1;
    memset(s,127,sizeof(s));
    top=1; s[1]=a[1]; s[0]=0-1<<30;
    for(i=2;i<=n;i++)
       {p=erf(i);
        f[i]=p+1; s[p+1]=min(s[p+1],a[i]); top=max(p+1,top);
        ans=max(ans,f[i]);
       }
    //for(i=1;i<=n;i++) printf("%d\n",f[i]);
    //printf("\n");
    printf("%d\n",n-ans);
}
void insert(int x,int y)
{
    zz++; e[zz].to=y; e[zz].pre=last[x]; last[x]=zz;
}
void find()
{
    int i,j,k,p;
    for(i=n;i>=0;i--)
       {g[i]=1<<30; insert(f[i],i);}
    g[0]=0; a[0]=0-(1<<30);
    for(i=1;i<=n;i++)
    for(j=last[f[i]-1];j;j=e[j].pre)
       {p=e[j].to;
        if(p>i) break;
        if(a[p]>a[i]) continue;
        for(k=p;k<=i;k++)
           {sm[k]=abs(a[p]-a[k]); sum[k]=abs(a[i]-a[k]);}
        for(k=p+1;k<=i;k++)
           {sm[k]+=sm[k-1]; sum[k]+=sum[k-1];}
        for(k=p;k<i;k++)
           g[i]=min(g[i],g[p]+sm[k]-sm[p]+sum[i]-sum[k]);
       }
    //for(i=1;i<=n;i++) printf("%d\n",g[i]);
    printf("%lld\n",g[n]);
}
int main()
{
    //freopen("sequence.in","r",stdin);
    //freopen("sequence.out","w",stdout);
    init(); dp(); find();
    return 0;
}

抱歉!评论已关闭.