前缀和差分
a[N]是b[N]的前缀和数组,b[N]是a[N]的差分数组
b[1]=a[1]
a[i]=b[1]+b[2]+······+b[i]
=a[1]+a[2]-a[1]+a[3]-a[2]+······+a[i]-a[i-1]
=a[i]
b[i]=a[i]-a[i-1]
例题
给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
输入格式
第一行输入正整数 n。
接下来 n行,每行输入一个整数,第 i+1 行的整数代表 ai
输出格式
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
数据范围
0<n≤105
0≤ai<2147483648
输入样例:
4
1
1
2
2
输出样例:
1
2
分析见注释
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n;
int a[N],b[N];
LL ans1,ans2;
/*
a[N]是b[N]的前缀和数组,b[N]是a[N]的差分数组
b[1]=a[1]
a[i]=b[1]+b[2]+······+b[i]
=a[1]+a[2]-a[1]+a[3]-a[2]+······+a[i]-a[i-1]
=a[i]
b[i]=a[i]-a[i-1]
枚举:
b[1]=a[1]
b[2]=a[2]-a[1]
b[3]=a[3]-a[2]
······
b[N]=a[N]-a[N-1]
由此可知:
使a数组中的数字完全相同 “等价于” 使b[2]~b[n]全为0
*/
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(i==1) b[i]=a[i];
else b[i]=a[i]-a[i-1];
}
/*
由于对[l,r]区间内的数字进行操作,即为:b[l]+=C,b[r+1]-=C;
即:对l,r+1进行操作
可进行的操作类型:
01:2<=l<=r<=n-1 ->尽量多使用
02: l=1,2<=r<=n-1
03: r=n,2<=l<=n
04: l=1,r=n -> 此操作无意义
*/
LL p,q;
q=p=0;
for(int i=2;i<=n;i++)
{
if(b[i]>0) p+=b[i];//正数的和
else q-=b[i];//负数的和
}
//q=abs(q);
ans1=min(p,q)+abs(q-p);//=max(q,p)
//由于每次只能+1/-1,所以给正负数配对,其余的只能选择02,03方案一次次改变
ans2=abs(p-q)+1;
//由于01方案不涉及b[1],故不会影响可能的结果
cout<<ans1<<endl<<ans2;
return 0;
}