首页 > 其他 > 详细

[HAOI2006]数字序列

时间:2019-03-29 22:29:53      阅读:151      评论:0      收藏:0      [点我收藏+]

Description:

给定一个整数序列,每次可以把一个数加大或减小,求:
1.最少操作几个数是原序列严格单增
2.在第一问的前提下,增大减小的总绝对值的最小值是多少

Hint:

\(n \le 10000\)

Solution:

这题是真的难,,,,,,

首先第一问应该都想得到,转化求保留最多数,就有:

\[f[i]=f[j]+1(a[i]-a[j]>=i-j)\]

把转移条件移项得:

\[a[i]-i>=a[j]-j\]

\(b[i]=a[i]-i\) ,就是求b的LIS

第二问

我们现在考虑对于两个相邻的\(b[i]\)

他们中间对应的原\(a[i]\)都是不合法的

所以需要调整

首先你要\(yy\)出一个十分不显然的结论:

对于中间一端的\(a[i]\)一定会变成一个分别沿着\(a[l]\)\(a[r]\)连续\(+1\)的阶梯状

且中间有一个分界点,换句话说,就是他们的\(b[i]\)不是变成b[l]就是\(b[r]\)

故我们可以枚举这个分界点\(k\)\(dp\),复杂度\(O(n^2)\)可以卡过

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=1e5+5,inf=1e9;
int n,m,bd,cnt,ans,hd[mxn];
int a[mxn],b[mxn],mi[mxn],longlen[mxn];
ll f[mxn],suml[mxn],sumr[mxn];

inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}

struct ed {
    int to,nxt;
}t[mxn<<1];

inline void add(int u,int v) {
    t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

int main()
{
    n=read();
    for(int i=1;i<=n;++i) a[i]=read(),b[i]=a[i]-i;
    b[n+1]=inf;
    for(int i=1;i<=n+1;++i) {
        int l=0,r=bd;
        while(l<r) {
            int mid=(l+r+1)>>1;
            if(mi[mid]<=b[i]) l=mid;
            else r=mid-1;
        }
        if(l==bd) ++bd; //普通LIS
        longlen[i]=l+1; add(longlen[i],i);/*为第二问预处理*/ mi[l+1]=b[i]; 
    }
    add(0,0); printf("%d\n",n-(bd-1));
    memset(f,0x3f,sizeof(f));
    b[0]=-inf; f[0]=0;
    for(int i=1;i<=n+1;++i) {
        for(int j=hd[longlen[i]-1];j;j=t[j].nxt) {
            int v=t[j].to;
            if(v>i||b[v]>b[i]) continue ; //转移条件
            suml[v]=0;
            for(int k=v+1;k<=i-1;++k) 
                suml[k]=suml[k-1]+abs(b[k]-b[v]); //下阶梯
            sumr[i-1]=0;
            for(int k=i-2;k>=v;--k) 
                sumr[k]=sumr[k+1]+abs(b[k+1]-b[i]);//下阶梯
            for(int k=v;k<=i-1;++k) 
                f[i]=min(f[i],f[v]+suml[k]+sumr[k]);
        }
    }
    printf("%lld",f[n+1]);
    return 0;
}

[HAOI2006]数字序列

原文:https://www.cnblogs.com/list1/p/10624560.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!