其他分享
首页 > 其他分享> > P2501 [HAOI2006]数字序列

P2501 [HAOI2006]数字序列

作者:互联网

洛咕

第一问

求最少改变多少个数,可以使得数列变为单调上升的序列

可以发现一个性质,在区间\([l,r]\)中,只有当\(a[r]-a[l]\ge r-l\)时,这个区间是合法的。

证明:举个例子即可证明。若\([1,5]\)的数字为\(1,5,7,3,4\),若是要保留\(a[1]\)和\(a[4]\),则\(5,7,3\)要改为\((1,4)\)中的整数且单调上升。但是\((1,4)\)中只有整数\(2,3\),少于3个。所以保留\(a[1]\)和\(a[4]\)不符合条件。

所以由上文可知,只有\(a[r]-a[l]\ge r-l\)时,\([l,r]\)才是合法的。

移项可知:

\[a[r]-r\ge a[l]-l\]

那么我们令\(b[i]=a[i]-i\),所以等式化为:

\[b[r]\ge b[l]\]

这个问题就转化为了求\(b[i]\)的最长不下降子序列,即为最多保留原数的个数。设\(f[i]\)表示末尾一个数为\(b[i]\)的最长不下降子序列长度。

\[ans=n-f[n]\]

第一问解决。


第二问

求在改变次数最少时,每个数改变的绝对值的和的最小值。

设\(g[i]\)表示\([1,i]\)的答案。

显然,当\(f[j]+1=f[i],j<i\)时,有

\[g[i]=min\{g[j]+w(j+1,i)\}\]

接下来求\(w(j+1,i)\)。

引理:\(\exists k\in [j+1,i]\),使得\(w(j+1,i)\)最小时,\(b[j+1\)到\(k]=b[j]\),\(b[k+1\)到\(i-1]=b[i]\)。

因为\(f[j]+1=f[i]\),所以\(k\)要么小于\(b[j]\),要么大于\(b[i]\),绝对不可能夹在\(b[j]\)和\(b[i]\)中间。

所以我们就可以知道\(b[k]\)必须要转变为端点值了。

在寻找\(k\)的时候,计算\(w(j+1,i)\)可以使用前缀和与后缀和,把\(n^3\)降低成\(n^2\),因为数据随机,所以最长不下降子序列期望为\(log_2n\),于是我们就可以水过这道题了。


上代码了

#include<bits/stdc++.h>
#define ll long long
#define N 35005
#define M 200005
#define INF (1<<30)
using namespace std;
struct no{
    int next,to;
}e[N<<1];
int head[N],cnt;
ll n,a[N],f[N],g[N],st[N],top;
ll sum[N],su[N],ans;
void add(int x,int y){
    e[++cnt].next=head[x];
    e[cnt].to=y;
    head[x]=cnt;
}
void solve1(){
    f[0]=0;a[0]=-INF;a[++n]=INF;
    f[1]=1;st[1]=a[1];top=1;//可以维护一个单调不降的栈,把LIS的复杂度降到O(nlogn)
    for(int i=2;i<=n;++i){
        if(st[top]<=a[i])st[++top]=a[i],f[i]=top;
        else{
            int tmp=upper_bound(st+1,st+top+1,a[i])-st;
            st[tmp]=a[i];
            f[i]=tmp;
        }
    }
    cout<<n-f[n]<<endl;
}
void solve2(){
    for(int i=0;i<=n;++i)add(f[i],i);//可以转化为图论快速找f[i]所对应的i
//  for(int i=0;i<=n;++i)cout<<f[i]<<" ";cout<<endl;
//  for(int i=1;i<=n;++i)g[i]=INF;
    memset(g,0x3f,sizeof g);g[0]=0;
    for(int i=1;i<=n;++i){
        for(int j=head[f[i]-1];j;j=e[j].next){
            int y=e[j].to;
            if(y>i||a[y]>a[i])continue;
            for(int k=y;k<=i;++k)sum[k]=abs(a[k]-a[y]),su[k]=abs(a[k]-a[i]);
            for(int k=y;k<=i;++k)sum[k]+=sum[k-1],su[k]+=su[k-1];
            for(int k=y;k<i;++k){
                g[i]=min(g[i],g[y]+sum[k]-sum[y]+su[i]-su[k]);//前缀和优化
            }
        }
    }
    cout<<g[n]<<endl;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i],a[i]-=i;
    solve1();
    solve2();
    return 0;
}

标签:所以,int,long,P2501,HAOI2006,ge,序列,define
来源: https://www.cnblogs.com/fenghuazhengmao/p/11581344.html