-
题目大意
给定一个序列,可以在其连续子序列上进行加一或者减一操作,求使整个序列变成相同数的最小操作数和种数。 -
思路
首要要明确是对[l,r]序列进行加一或者减一操作,可以联想到前缀和,我们对一个数组b的第l项进行减一操作,第r+1项进行加一操作,这样作用到b数组的前缀和序列就是连续的区间段[l,r]内全部进行减一操作。
这样可以把o(m*n)的时间复杂度转化为o(n)。
这时候就要利用到差分,之前我也不是很懂,虽然它很简单昨天睡觉前又看了一遍才彻底搞明白。
差分:
对于序列A为:a1,a2,a3,……,an;
A的差分序列B:b1,b2,b3,……,bn;
b1=a1,b2=a2-a1,bi=ai-a(i-1);
而等价于ai=b1+b2+……+bi=a1+a2-a1+……+ai-ai-1=ai;
所以可以得到序列B是A的差分序列,而序列A为B的前缀和
结合上面所说,对A连续子序列进行加一减一操作可以转化为对b[l]和b[r+1]进行操作。
求出差分序列之后,原问题可以转化为将序列B从第二项开始到最后一项全部变为0的最小操作数和种数。
这是用到贪心策略,将一个有正有负的序列进行加减最后变为0,而且题中不考虑实现过程,只要求最后结果。最小操作数就是将正数进行减操作,负数进行加操作,在[2,n]区间中任取一个正数和一个负数进行操作。最后序列剩下全正或者全负,此时种数就是剩下的数加0,相当于序列A变成的值可以为0~k,(k是剩余序列B的和)
这里再说一下最小操作数,因为之前我这里就不是很明白,将正数减c操作就相当于序列A从当前位置一直到最后这个连续区间段都进行了c次减一操作,操作步数为c,而找一个负数与正数搭配,相当于在正数与负数这个区间内进行c次减一或者加一操作。
最后最小的操作步数就是min(p,q)+abs(p-q),也就相当于max(p,q),种数就是abs(p-q)+1;p为序列B中正数和,q为负数和 -
代码
注意要用long long
#include <bits/stdc++.h>
using namespace std;
long long a[100010];
int main()
{
long long n;
cin>>n;
for(int i=0; i<n; i++)
cin>>a[i];
long long p=0,q=0;
for(int i=n-1; i>0; i--)
{
a[i]-=a[i-1];
///cout<<a[i]<<" ";
if(a[i]>=0)
p+=a[i];
else
q-=a[i];
}
long long ans=max(p,q);
long long res=abs(p-q)+1;
cout<<ans<<endl;
cout<<res<<endl;
return 0;
}