题解P4552
题目描述
给定一个长度为\(n\)的数列,每次可以选择区间 \([l,r]\), 使得这个区间内的数都加一或者减一。
请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。
输入输出
第一行一个正整数\(n\)接下来 \(n\) 行,每行一个整数,第\(i + 1\) 行的整数表示\(a_i\)。
第一行输出最少操作次数
第二行输出最终能得到多少种结果
样例数据
\(in\)
6
4 8 6 5 10 7
\(out\)
9
4
题解
因为是区间加减,我们就考虑到使用差分数组。即新定义一个数组\(c\),使得\(c[i] = a[i+1] - a[i]\)
这样,看到样例,我们就有
\[ c = \{ 4 , -2 , -1 , 5 , -3 \} \]
对于区间\([l,r]\),加1减1的操作对c数组的影响为
![](/qrcode.jpg)
\[ [l,r]都加一:a[l-1]++,a[r]-- \\ [l,r]都减一:a[l-1]--,a[r]++ \]
取\(x,y\),使得\(x\)为\(c\)中所有整数之和,\(y\)为\(c\)中所有负数之和再取反。观察前面的结论,我们可以把操作分成两部分:先是正负抵消,剩下的最后一个数,再单独把这个数递减(或加)到零,所以操作次数就是\(max(x,y)\)
举例
c = { 4 , -2 , -1 , 5 , -3 }
c = { 2 , 0 , -1 , 5 , -3 }
c = { 1 , 0 , 0 , 5 , -3 }
c = { 0 , 0 , 0 , 5 , -2 }
c = { 0 , 0 , 0 , 0 , 3 }
第二问。回顾操作两个步骤:“先是正负抵消,剩下的最后一个数,再单独把这个数递减(或加)到零。”那么我们第一步进行完以后一定会得到这个数组:
\[ c = \{0,0,0,0,0,···,0,0,x-y \} \]
当\(x-y > 0\)时,我们可以选择将区间\([1,n]\)都加\(x-y\),或者将区间\([n,n]\) 减\(x-y\),这两种操作得到的答案就是最终的答案区间,这个区间覆盖的数字个数就是\(|x-y| + 1\)。所以最终答案为\(|x-y| + 1\)。
同理反之亦然。
为什么不用模拟操作?
因为我们已经直接算出结果来了鸭
AC代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
long long max(long long a,long long b){return a > b ? a : b;}
long long n;
long long a[100099];
long long c[100099];
int main(){
cin >> n;
for(long long i = 1;i <= n; i++) cin >> a[i];
long long x,y;
x = y = 0;
for(long long i = 1;i < n; i++){
c[i] = a[i+1] - a[i];
if(c[i] < 0){
x -= c[i];
}else{
y += c[i];
}
}
cout << max(x,y) << endl;
cout << abs(x-y) + 1 << endl;
return 0;
}