bzoj1049 [HAOI2006]数字序列 ( LIS + 区间DP)

来源:互联网 发布:淘宝首页在线制作 编辑:程序博客网 时间:2024/06/09 20:39

bzoj1049 [HAOI2006]数字序列

原题地址:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1049

题意:
现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。
但是不希望改变过多的数,也不希望改变的幅度太大。
求:
1.最少需要改变多少个数
2.在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

数据范围
n<=35000,保证所有数列是随机的

题解:
ydc神犇的题解

神题,神结论。
O(n^3)水过随机数据。

首先对于第一问,求最少需要改变多少个数:
补集转化的思想,即求 n-(最多有多少个数不改变),
就是求最长严格上升子序列长度,用二分+单调栈即可。
(但是这里有一个转化,就是 数字-标号,转为求最长不降子序列,在第二问中有用)

对于第二问:
之前在第一问中,每个数都 数字-标号,转为求最长不降子序列了,之后得到一个f[i]数组
f[i]表示,以i结尾的最长不降子序列长度。
设 g[i]=以i结尾的最长不降子序列,从1到i,要全部转为不降的最小代价。

那么怎么求g[i]呢?

考虑最原始的求解最长不降子序列的方法。
是一个n^2的DP,f[i]由前面最大的f[j]+1满足 a[j]<=a[i]转移过来。
就是说,取i的前一个保留位置是j,( i,j )区间的全部都要改变使[i,j]不降。

于是得到一个转移方程:
令w[i,j]表示保留i,j,使区间[i,j]合法的最小代价。
g[i]=min( g[j]+w[j,i] ) ( f[i]==f[j]+1 且 a[i]>=a[j] )

w[i,j]怎么算?
有一个非常重要的结论:
对于[i+1,j]区间,必然存在一个断点m,使得如果让 i+1到m-1都变成 i,m到j都变成 j ,是保留i,j,使区间[i,j]合法的最小代价。
证明见ydc神犇
这里写图片描述
于是w[i,j]就可以枚举断点来求。

总复杂度 O(n^3) ,本来是会T的,
但是对于找到 f[i]==f[j]+1 且 a[i]>=a[j] 时,可以把 f[i]=x预先存下来,因为数列是随机的,可以水过。

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>#define LL long longusing namespace std;const int N=36000;const int inf=0x3f3f3f3f;int n,stack[N],top=0,a[N],f[N];LL g[N],s1[N],s2[N];vector<int> V[N];int find(int x){    int lf=1; int rg=top;    while(lf+1<rg)    {        int mid=(lf+rg)>>1;        if(stack[mid]<=x) lf=mid;        else rg=mid;    }    if(stack[lf]>x) return lf;    else return rg;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    {scanf("%d",&a[i]); a[i]=a[i]-i;}    for(int i=1;i<=n;i++)    {        if(top==0||stack[top]<=a[i]) {stack[++top]=a[i];f[i]=top;}        else if(stack[1]>a[i]) {stack[1]=a[i]; f[i]=1;}        else {f[i]=find(a[i]);stack[f[i]]=a[i];}        V[f[i]].push_back(i);    }    printf("%d\n",n-top);    for(int i=0;i<=n+1;i++) g[i]=1LL<<60;    V[0].push_back(0); a[0]=-inf; g[0]=0;     V[top+1].push_back(n+1); a[n+1]=inf; f[n+1]=top+1;    for(int i=1;i<=n+1;i++)    {        int sz=V[f[i]-1].size();        for(int j=0;j<sz;j++)        {            int x=V[f[i]-1][j];            if(x>i) break;            if(a[x]>a[i]) continue;            s1[x]=s2[x]=0;            for(int k=x+1;k<=i;k++)            {                s1[k]=abs(a[x]-a[k]);                 s2[k]=abs(a[i]-a[k]);            }            for(int k=x+1;k<=i;k++)            {                s1[k]=s1[k-1]+s1[k];                s2[k]=s2[k-1]+s2[k];            }            for(int k=x+1;k<=i;k++)             g[i]=min(g[i],g[x]+s1[k-1]-s1[x]+s2[i]-s2[k-1]);        }    }    printf("%I64d\n",g[n+1]);    return 0;}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 买电脑被坑了怎么办啊 在电脑城被坑了怎么办 小狗20天不睁眼怎么办 金星秀停播沈南怎么办 干了活拿不到钱怎么办 干完活要不到钱怎么办 让蚊子咬了很痒怎么办 秋天被蚊子咬了怎么办 练芭蕾脚背太硬怎么办 杨梅酒里的杨梅怎么办 月经期喝了啤酒怎么办 泡过酒的樱桃怎么办 泡了酒的桑葚怎么办 不知怀孕喝了酒怎么办 香氛蜡烛记忆环怎么办 我吃了马兜铃怎么办呀 肝肾衰弱有毒素怎么办 肾阴虚阳虚都有怎么办 吃辣刺激胃疼怎么办 舌头没有舌苔有裂纹疼痛怎么办 舌苔厚黄是怎么回事且口臭怎么办 舌苔厚白是怎么回事且口臭怎么办 长期有舌苔白厚怎么办 小孩的舌苔厚白怎么办 口苦口臭舌苔黄怎么办 婴儿的舌苔厚白怎么办 舌苔黄厚口臭痒怎么办 想让月经提前来怎么办 宝宝拉肚子怎么办吃什么好 投资p2p跑路了怎么办 借钱不还怎么办最有效 朋友借小钱不还怎么办 网络上贷款不还怎么办 网贷实在还不了怎么办 娱乐平台跑路了怎么办 360借条被拒了怎么办 网贷注册太多了怎么办 汽车大绿本丢了怎么办 网贷平台跑路怎么办 电脑中了1kb病毒怎么办 360网页走丢了怎么办