思路
1.差分定义
当看到区间加减问题的时候我们就要考虑前缀和或者差分,首先我们要清楚差分的定义
diff[1] = a[1]
diff[i] = a[i] - ai - 1
2.问题转化
差分数组除了第一项以外,其余的每一项都是当前项减去前一项的差,那么如果我想要令数列的数都一样,本质上就是使得diff[2]~diff[n]全部变成0,不太明白可以再看看下面这个公式,如果a[i] == a[i - 1],那么diff[i] = 0,再推广到全局
diff[i] = a[i] - a[i - 1] = 0 (2 <= i)
(注意,到此为止,题目已经和原数组没有关系了,不要再去考虑原数组了,现在要考虑的是怎么把差分数组从第2项~第n项全部变为0)
3.最小操作次数:
我们已经把一个原数组区间加减问题转化成了将差分数组2~n项变成0的问题了。
怎么样快速把一个数组全部变为0呢,利用贪心的思想每次让这个数组里的正数减一,然后负数加1,是不是一次性就可以让两个数向0靠近,这一步算一次操作,也是最快的操作。
那么总共有几次这种操作呢?答案就是min(p,q),p是差分数组2 ~ n项中所有正数的和,q是差分数组2 ~ n项中负数的和。如果差分数组里面的正数和p和负数和q,如果p比q小,在执行完min(q,p)次操作后.那么数组里面就只有负数了,否则就只有正数。
此时差分数组里面剩下的就只有一些刺头,只能一次一次的对它们做diff[i]- 1或者diff[i]+1操作(取决于他们是正数还是负数,趋向0做操作),总共有多少次呢?答案就是abs(p - q)次。综上所述min(p,q) + abs(p - q) = max(p,q)
4.种类数量:
当我们执行完最小操作次数中的负数+1,正数-1的操作之后,剩余刺头就需要我们一个一个减去或者加上,直到它们全部为0,那我们是不是可以在减去或者加上这些刺头的途中,顺便对diff[1]进行操作呢,因为diff[1]就是我们数列的最终元素,只要我的diff[1]每次都不一样就是一种答案。那我最多可以顺便对diff[1]操作几次呢,那最多肯定是abs(p-q)次吧,如果再多操作一次,是不是就不满足第一个最小操作次数的答案要求了。那我在对刺头进行操作的时候,是不是可以不对diff[1]操作呢,这也是一种答案,所以最终答案就是abs(p-q)+1;
没有和别的大佬考虑diff[n+1]的情况,本身只要理解了题目就好,第一次写题解,嘤嘤嘤,如果有不足的地方请指出。
题解
int main(void)
{
cin >> n;
for(int i = 1;i <= n;i ++) cin >> a[i];
for(int i = 1;i <= n;i ++) diff[i] = a[i] - a[i - 1];
LL p = 0,q = 0;
for(int i = 2;i <= n;i ++)
{
if(diff[i] > 0) p += diff[i];
else q -= diff[i];
}
cout << max(p,q) << endl;
cout << abs(p - q) + 1 << endl;
return 0;
}