给定一个长度为 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。
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
4
1
1
2
2
1
2
求出 a a a 的差分序列 b b b,其中 b 1 = a 1 , b i = a i − a i − 1 ( 2 ≤ i ≤ n ) b_1 = a_1,b_i = a_i - a_{i-1} (2 \le i \le n) b1=a1,bi=ai−ai−1(2≤i≤n)。令 b n + 1 = 0 b_{n+1} = 0 bn+1=0。
题目对序列 a a a 的操作,相当于每次选出 b 1 , b 2 , . . . , b n + 1 b_1, b_2, ..., b_{n+1} b1,b2,...,bn+1 中的任意两个数,一个加1,另一个减1。目标是把 b 2 , b 3 , . . . , b n b_2, b_3,..., b_n b2,b3,...,bn 变为全0。最终得到的数列 a a a 就是由 n n n 个 b 1 b_1 b1 构成的。
从 b 1 , b 2 , . . . , b n + 1 b_1, b_2, ..., b_{n+1} b1,b2,...,bn+1 中任选两个数的方法可分为四类。
设 b 2 , b 3 , . . . , b n b_2, b_3, ..., b_n b2,b3,...,bn 中正数总和为 p p p,负数总和为的绝对值为 q q q。首先以正负数配对的方式尽量执行第 1 类操作,可执行 m i n ( p , q ) min(p, q) min(p,q) 次。剩余 ∣ p − q ∣ |p - q| ∣p−q∣ 个未配对,每个可以选与 b 1 b_1 b1 或 b n + 1 b_{n + 1} bn+1 配对,即执行第 2 类或第 3 类操作,共需 ∣ p − q ∣ |p - q| ∣p−q∣ 次。
综上所述,最少操作次数为 m i n ( p , q ) + ∣ p − q ∣ = m a x ( p , q ) min(p, q) + |p - q| = max(p, q) min(p,q)+∣p−q∣=max(p,q) 次。根据 ∣ p − q ∣ |p - q| ∣p−q∣ 次第2、第 3 类操作的选择情况,能产生 ∣ p − q ∣ + 1 |p - q| + 1 ∣p−q∣+1 种不同的 b 1 b_1 b1 的值(原本的 b 1 b_1 b1 是一种),即最终得到的序列 a a a 可能有 ∣ p − q ∣ + 1 |p - q| + 1 ∣p−q∣+1 种。
#include
#include
using namespace std;
#define ll long long
const int N = 100010;
ll a[N]; //原数组
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
//求差分序列:倒着求,因为要用到i-1位置的值,而该值是原数组的值
for (int i = n; i > 1; i--) a[i] -= a[i - 1];
//因为数组中值比较大,可能会溢出,所以使用long long
//pos正数和,neg负数和
ll pos = 0, neg = 0;
//求拆分数组中所有正数的和 和 所有负数的和
for (int i = 2; i <= n; i++) {
if (a[i] > 0) pos += a[i];
else neg -= a[i];
}
cout << min(pos, neg) + abs(pos - neg) << endl;
cout << abs(pos - neg) + 1 << endl;
return 0;
}