IncDec Sequence(差分)
题目
给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
输入格式
第一行输入正整数 n。
接下来 n 行,每行输入一个整数,第 i+1 行的整数代表 ai。
输出格式
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
数据范围
0<n≤105,
0≤ai<2147483648
输入样例:
4
1
1
2
2
输出样例:
1
2
分析
求出 a 的差分数组 b ,令
b
n
+
1
b_{n + 1}
bn+1 = 0。题目对于a数组的操作,相当于每次选出
b
1
b_{1}
b1,
b
2
b_{2}
b2…
b
n
+
1
b_{n + 1}
bn+1 中的任意两个数,一个 +1,一个 -1。
目标是把
b
2
b_{2}
b2,
b
3
b_{3}
b3…
b
n
b_{n}
bn 全部变为0。最终得到的数组a 就是由n个
b
1
b_{1}
b1 构成的。
从
b
1
b_{1}
b1,
b
2
b_{2}
b2…
b
n
b_{n}
bn 中任选两个数的方法可以分为四类:
1.选
b
i
b_{i}
bi 和
b
j
b_{j}
bj(2
≤
\leq
≤ i,j
≤
\leq
≤ n)。这种操作会改变
b
2
b_{2}
b2,
b
3
b_{3}
b3…
b
n
b_{n}
bn 中两个数的值。应该保证 b[i] 和 b[j] 一正一负的前提下,尽量多的采取这种操作,更快的接近目标。
2.选
b
1
b_{1}
b1 和
b
j
b_{j}
bj(2
≤
\leq
≤ j
≤
\leq
≤n).
3.选
b
2
b_{2}
b2 和
b
n
+
1
b_{n + 1}
bn+1(2
≤
\leq
≤ i
≤
\leq
≤ n)。
4.选
b
1
b_{1}
b1 和
b
n
+
1
b_{n + 1}
bn+1.此情况无意义,因为他不会改变 b[2],b[3]…b[n] 的值,相当于浪费了一次操作,必然不是最优解。
设
b
2
b_{2}
b2,
b
3
b_{3}
b3…
b
n
b_{n}
bn 中正数的和为 res1 ,负数和的绝对值为 res0。首先以正负数配对的方式进行尽量执行第一类操作,
可执行 min(res1,res0)次。剩余
∣
\mid
∣res1 - res0
∣
\mid
∣ 个未配对,每个可以选与
b
1
b_{1}
b1 或
b
n
+
1
b_{n + 1}
bn+1 配对,即执行第2或3类操作,共需
∣
\mid
∣ res1 - res0
∣
\mid
∣ 次。
综上,最少操作次数为 min(res1,res0) +
∣
\mid
∣ res1 - res0
∣
\mid
∣。根据
∣
\mid
∣ res1 - res0
∣
\mid
∣ 次 第2、3类操作的选择情况,能产生
∣
\mid
∣ res1 - res0
∣
\mid
∣ + 1 种不同的
b
1
b_{1}
b1 值,即最终得到的数组 a 可能有
∣
\mid
∣ res1 - res0
∣
\mid
∣ + 1 种。
题解
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define int long long
typedef pair<int, int> PII;
const int N = 1e6 + 10, M = 5e6 + 10, P = 131;
const int INF = 0x3f3f3f3f;
int n;
int a[N], b[N];
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
b[i] = a[i] - a[i - 1];
}
int res1 = 0, res0 = 0;
for (int i = 2; i <= n; i++)
{
if (b[i] > 0)
res1 += b[i];
else if (b[i] < 0)
res0 -= b[i];
}
cout << max(res0, res1) << endl;
cout << abs(res1 - res0) + 1 << endl;
}