首页 > 技术文章 > 增减序列

guiyou 2021-08-11 14:15 原文

增减序列

最终的目的是让整个序列相等

操作只有一种使\([l,r]\)区间全都\(+1\)

既然整个序列相等,那么整个序列的差分序列应为0

操作的话就可以转化为\(a[l]+=1,a[r+1]-=1\)

求的是最少操作次数

通过原数列,我们先求一个原始的差分序列,然后每次操作使,正数-1,负数+1,这样就使差分序列往目标:靠近 0序列
(当然差分序列第1项不用为0,而且首项可以为任何数,思考为什么?)

设正数的和p,负数绝对值和q,那么就很容易的到一个最少操作次数 \(max(p,q)\)
(简单描述下怎么来的,操作\(min(p,q)\)次后,p,q中小的变为0,还剩\(abs(p-q)\)次 ,所以最少操作次数 \(min(p,q)+abs(p-q)=max(p,q)\))

至于最少操作后的结果

操作\(min(p,q)\)次后,那么一定剩下几项不为0
此时可以考虑两中情况

  1. 与差分序列首项想相加减
    这种情况因为改变首项,所以最终序列会改变,故会得到\(abs(p-q)\)种序列

  2. 与第\(n+1\)项相加减
    这种情况并不改变首项,只有一种序列(第n+1项无论怎么变,都不影响最终序列,也可以思考为什么)

总的来说会得到\(abs(p-q)+1\)种方案

  • code
#include<iostream>

using namespace std;
const int maxn=1e5+10;
int a[maxn];int n;
int cf[maxn];
long long p=0,q=0;
long abs(long x){
    return x<0?-x:x;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        cf[i]=a[i]-a[i-1];
    }
    for(int i=2;i<=n;++i){
        if(cf[i]>0) p+=cf[i];
        if(cf[i]<0) q-=cf[i];
    }
    cout<<max(p,q)<<endl<<abs(p-q)+1<<endl;return 0;
}

推荐阅读