题目链接:https://www.acwing.com/problem/content/description/102/
时空限制:10s / 256MB
题目描述
给定一个长度为 n 的数列a1,a2,…,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
输入格式
第一行输入正整数n。
接下来n行,每行输入一个整数,第i+1行的整数代表ai。
输出格式
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
数据范围
0<n≤10^5, 0≤ai<2147483648
输入样例
4
1
1
2
2
输出样例
1
2
解题思路
首先我们来介绍一下差分是什么东西:
1. 差分定义
- 定义一个差分数组S和一个原数组A。
- p[1]=a[1], p[i]=a[i]-a[i-1]
2. 差分性质
- A数组的差分数组的前缀和数组就等于原数组,即S[i]=a[i]=p[i]+S[i-1]
- S数组的差分数也等于差分数组,即p[i]=S[i]-S[i-1]
- 还有比较重要,而且常用的的一条,我们在修改某一段区间的时候,比如这道题的集体加 e 的时候,将操作放在差分数组上就变成了p[l]+=e,p[r+1]-=e这样,我们就可以把区间操作更改为单点操作,很大的减少了时间复杂度。
- 当然,差分的运用远不止于此,在树上等也是可以进行差分操作的。
综上:有了这些性质,我们在做一些题的时候就可以将其转换为差分数组求解,完成过后过一遍前缀和得到正确答案。
说了这么多,就来看看这一题怎样做吧。
3. 本题题解
- 因为是区间整体加减1,所以我们很自然的就可以想到用差分求解。
- 这道题可以看做求出原序列的差分之后,将差分数组p[2...n]全部变为0所需的最少的步数和可以使p[1]变为多少种不同的数。
- 贪心操作: 很明显,在我们求出的差分数组中,有正数也有负数,要消除这些数,使得它们全部归零。,我们可以让正数减1,负数加1,这样就可以快速的转化成目标0。有可能pos!=neg,那么就让剩余的和p[1]或p[n+1]配对。
- 经过上述分析,我们就能够推导出本题的最小次数公式:
- ans=min(pos,neg)+abs(pos-neg)=max(pos,neg);
- 最后我们还要求能构成几组解, 这很容易可以推出: ans=abs(pos-neg)+1
Accepted Code:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b, n, cnt;
long long pos = 0, neg = 0;
scanf("%d", &n);
scanf("%d", &a);
for (int i = 1; i < n; i++) {
scanf("%d", &b);
cnt = b - a;
if (cnt > 0)
pos += cnt;
else neg -= cnt;
a = b;
}
printf("%lld\n%lld\n", max(pos, neg), llabs(pos - neg) + 1);
return 0;
}