Solution IncDec Sequence
题目大意:给定一个序列,你可以将一段子序列集体\(+1\)或\(-1\),求最小的操作次数使得所有数相等,以及操作方案数
分析:
区间加减不好考虑,我们可以考虑差分
令原数组\(val[0] = 0\),差分数组\(d[i] = val[i] - val[i - 1]\)
然后问题就变成了,给你一个序列,每次可以将\(1\)个数\(+1\)同时将另一个数\(-1\),求最小操作次数使得所有数下标不为\(1\)的数为\(0\)
我们发现,这种方式可以同时改变\(2\)个数,应当优先采用
设\(q = \sum d[i] \quad | \quad d[i] > 0 \; and \; i\neq 1\),\(p = \sum d[i] \quad | \quad d[i] < 0 \; and \; i\neq 1\)
这种方法最多采用\(min(p,q)\)次
然后考虑剩下的,这个需要\(|p-q|\)次
所以需要\(min(p,q) + |p -q| = max(p,q)\)次
方案:
方案取决于剩下的数,显然剩下的都和\(d[1]\)同号了,有些可以作用于\(d[1]\),有些可以作用于\(d[n + 1]\),所以有\(|p - q| + 1\)种不同的\(d[1]\)取值,所以方案有\(|p - q| + 1\)种
#include <algorithm>
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100;
ll val[maxn],d[maxn],sum,vsum;
int n;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++)scanf("%lld",val + i);
for(int i = 2;i <= n;i++){
d[i] = val[i] - val[i - 1];
if(d[i] > 0)sum += d[i];
else vsum -= d[i];
}
printf("%lld\n",max(sum,vsum));
printf("%lld\n",abs(sum - vsum) + 1);
return 0;
}