题目地址:
https://www.acwing.com/problem/content/102/
给定一个长度为 n n n的数列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an,每次可以选择一个区间 [ l , r ] [l,r] [l,r],使下标在这个区间内的数都加一或者都减一。求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
输入格式:
第一行输入正整数
n
n
n。
接下来
n
n
n行,每行输入一个整数,第
i
+
1
i+1
i+1行的整数代表
a
i
a_i
ai。
输出格式:
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
数据范围:
0
<
n
≤
1
0
5
0<n≤10^5
0<n≤105
0
≤
a
i
<
2147483648
0≤a_i<2147483648
0≤ai<2147483648
首先求一下 a a a的差分数组 b b b,即 b i = a i − a i − 1 b_i=a_i-a_{i-1} bi=ai−ai−1。那么将某个区间 [ l , r ] [l,r] [l,r]的数都加 1 1 1等价于将 b [ l ] b[l] b[l]加 1 1 1并且将 b [ r + 1 ] b[r+1] b[r+1]减 1 1 1;而如果将某个区间 [ l , r ] [l,r] [l,r]的数都减 1 1 1等价于将 b [ l ] b[l] b[l]减 1 1 1并且将 b [ r + 1 ] b[r+1] b[r+1]加 1 1 1。我们首先考虑最少操作次数应该是多少。如果最后真的让所有的数都变成了一样,那么 b b b会满足 b 2 = b 3 = . . . = b n = 0 b_2=b_3=...=b_n=0 b2=b3=...=bn=0。我们不妨设 ∑ b i > 0 b i = p , − ∑ b i < 0 b i = q \sum_{b_i>0}b_i=p,-\sum_{b_i<0}b_i=q ∑bi>0bi=p,−∑bi<0bi=q,如果 p > q p>q p>q,那么我们可以先每次都从 b 2 ∼ n + 1 b_{2\sim n+1} b2∼n+1中选取一正一负,正的减 1 1 1,负的加 1 1 1,先把 b b b中的负数全消灭掉,接下来如果还有非零的数,不妨设这些数都是负的,这样可以通过调整 b 1 b_1 b1和 b n + 1 b_{n+1} bn+1的办法将这些负数也都变为 0 0 0,最终就达到了将 b b b调整为满足条件的序列的结果。这种操作方式的次数为 min { p , q } + ∣ p − q ∣ \min\{p,q\}+|p-q| min{p,q}+∣p−q∣,并且一定是次数最少的,因为要将 b 2 ∼ n b_{2\sim n} b2∼n都变为 0 0 0的话,正负抵消一定是最快的,抵消不掉的则需要借助 b 1 b_1 b1。最后得到的数列的个数即取决于 b 1 b_1 b1变成了什么,因为正负抵消掉之后的调整过程,可以调整 b 1 b_1 b1也可以调整 b n + 1 b_{n+1} bn+1,所以一共有 ∣ p − q ∣ + 1 |p-q|+1 ∣p−q∣+1种可能。代码如下:
#include <iostream>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int a[N];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = n; i > 1; i--) a[i] -= a[i - 1];
ll pos = 0, neg = 0;
for (int i = 2; i <= n; i++)
if (a[i] > 0) pos += a[i];
else neg -= a[i];
printf("%lld\n", min(pos, neg) + abs(pos - neg));
printf("%lld\n", abs(pos - neg) + 1);
}
时空复杂度 O ( n ) O(n) O(n)。