##[$AcWing$ $100$. 增减序列](https://www.acwing.com/problem/content/102/) ### 一、题目描述 给定一个长度为 $n$ 的数列 $a_1,a_2,…,a_n$,每次可以选择一个区间 $[l,r]$,使下标在这个区间内的数都加一或者都减一。 求至少需要多少次操作才能 **使数列中的所有数都一样**,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。 **输入格式** 第一行输入正整数 $n$。 接下来 $n$ 行,每行输入一个整数,第 $i+1$ 行的整数代表 $a_i$。 **输出格式** 第一行输出最少操作次数。 第二行输出最终能得到多少种结果。 **数据范围** $0 **转化**:题目对 $a$ 序列的 **任何区间加减$1$操作**,等同于对 $b$ 序列中的 **任意两数**,一个加 $1$,一个减 $1$。 > **目标** : 把 $b[2]$ 至 $b[n]$ 全变为 $0$,$b[1],b[n+1]$是多少无所谓。 从 $b[1]$ 至 $b[n+1]$ 中 **任选** 两数的方案进行分类讨论: 由于$b[1],b[n+1]$比较特殊,所以划分分类时,以它们为分类标准进行划分四类: - **① 选 $b[i]$ 和 $b[j]$** 其中 $2≤i,j≤n$,这种操作会改变 $b[2]$ 至 $b[n]$ 中两个数的值,因为差分操作,无外乎就是: - 前项$+1$,后项$-1$ - 前项$-1$,后项$+1$ 这两种情况,目标是把这段区间所有数字变成$0$,那大于$0$的得$-1$向小了变,小于$0$的得$+1$向大的变。也就是尽量挑 $b[i]$ 和 $b[j]$ **一正一负** 的情况下,这样一下就可以 **变掉两个数**,使得整体向都是$0$的区间逼近。 - **② 选 $b[1]$ 和 $b[j]$** 其中 $2≤j≤n$;这种情况下也是两种可能分支: - $b[j]>0$,那么$b[j]-=1$,$b[1]+=1$ - $b[j]<0$,那么$b[j]+=1$,$b[1]-=1$ 因为我们并不关心$b[1]$的最终值是多少,只关心尽快的把b[2 \sim n]变成$0$,所以,这样一下只能改一个数字,效率不如上面的 ① 快。 - **③ 选 $b[i]$ 和 $b[n+1]$** 其中 $2≤j≤n$;这种情况下也是两种可能分支: - $b[i]>0$,那么$b[i]-=1$,$b[n+1]+=1$ - $b[i]<0$,那么$b[i]+=1$,$b[n+1]-=1$ 因为我们并不关心$b[n+1]$的最终值是多少,只关心尽快的把$b[2 \sim n]$变成$0$,所以,这样一下只能改一个数字,效率不如上面的 ① 快。 - **④ 选 $b[1]$ 和 $b[n+1]$** 因为它不会改变 $b[2]$ 至 $b[n]$ 的值,浪费了操作,一定不是最优解,放弃这种可能。 所以,我们的贪心策略就是:尽量的采用方法 ①,如果实在没有办法使用方法 ①时,再考虑使用方法 ② 或者 方法 ③。 那什么情况下可以使用方法 ① 呢? 限制条件是在$b[2 \sim n]$中存在$1$个及$1$个以上大于$0$的数字,并且,存在$1$个及$1$个以上小于$0$的数字,才能进行此操作。 所以,有了如下的神操作: **设 $b[2]$ 至 $b[n]$ 中正数总和为 $p$,负数总和的绝对值为 $q$。** 首先,我们以正负配对的方式执行操作,可执行 $min(p,q)$ 次,剩余 $|p−q|$ 个没有配对。 接下来,剩下的每个与 $b[1]$ 或 $b[n+1]$ 配对,共需 $|p−q|$ 次。 **综上**,最少操作次数为 $min(p,q)+|p−q|=max(p,q)$ 次。 下面来思考第二个问题: > **保证最少次数的前提下,最终得到的数列可能有多少种?** 答:在尽量采用方法 ① 的情况下,可以尽快的使得整体区间向全是$0$进行逼近,但最后,随着正数或者负数的消亡,剩下了$|p-q|$个正数值或者负数值,此时,只能通过与$b[1]$或 $b[n+1]$的 **捉对** 操作才能完成整体全是$0$的目标 假设剩下$|p-q|$个: - $b[1]$消耗掉$0$个,$|p-q|$个交给$b[n+1]$消耗掉 - $b[1]$消耗掉$1$个,$|p-q|-1$个交给$b[n+1]$消耗掉 - $b[1]$消耗掉$2$个,$|p-q|-2$个交给$b[n+1]$消耗掉 ... - $b[1]$消耗掉$|p-q|$个,$0$个交给$b[n+1]$消耗掉 - 共有$|p-q|+1$种情况,此时,$b[1]$的值也随之变化,共$|p-q|+1$种结果。 ### 三、实现代码 ```cpp {.line-numbers} #include using namespace std; typedef long long LL; const int N = 100010; int n; int a[N], b[N]; int main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; b[i] = a[i] - a[i - 1]; } LL p = 0, q = 0; for (int i = 2; i <= n; i++) if (b[i] > 0) p += b[i]; else q -= b[i]; printf("%lld\n%lld\n", max(p, q), abs(p - q) + 1); return 0; } ```