【ACwing 100】InDec序列——差分

(题面来自AcWing)

给定一个长度为 n 的数列 a1,a2,,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。

求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。

输入格式

第一行输入正整数n

接下来n行,每行输入一个整数,第i+1行的整数代表ai

输出格式

第一行输出最少操作次数。

第二行输出最终能得到多少种结果。

数据范围

0<n105,
0ai<2147483648(因为不开long long再次见到了祖宗)

   

  看到题面之后的第一想法是差分之后暴搜方案,每次从(1~n + 1)选不同的两个数+1/-1,目标状态是d_2~d_n全部为0。然而数据范围显然不能接受。考虑序列操作的本质:我们要将差分序列d中除第一项外全部变为0,显然最优的方案是在d_2~d_n中优先选择一个负数、一个正数,分别+1、-1;设p、q分别为d中正、负项之和的绝对值,那么如上的第一种操作会把p、q中较小的一方降为0,这个过程最多执行min(p, q)次。之后正/负项中有一方会有剩余,我们可以选择把d_1与剩余项一同操作(相当于把原序列的某个前缀全部+1/-1),也可以选择d_(n+1)与剩余项一同操作(相当于操作后缀),最终都可以实现d_2~d_n全部为0。这一步操作最多执行|p - q|次。那么总的操作次数ans1满足:

  ans1 = min(p, q) + |p - q| = max(p, q)

  第二问要求统计在满足最小步数的情况下,可能产生的序列种类。由于最后序列每一项都相同,而d[1] = a[1],我们只要考虑最后d[1]有多少种即可。显然,上述最短操作过程不分先后,并且第一种操作不会对d[1]产生影响。考虑执行第二种操作后d[1]可能的取值:d[1]最多被增加/减少|p - q|次,最少被操作0次,并且d[1]可以被操作[0, |p - q|]中的任意多次。因此d[1]的取值有|p - q| + 1种,即ans2 = |p - q| + 1。

代码:

  1. #include <cstdio>  
  2. #include <iostream>  
  3. #include <cmath>  
  4. #define rep(i, a, b) for(int i = a; i <= b; ++i)  
  5. #define per(i, b, a) for(int i = b; i >= a; --i)  
  6. #define maxn 100010  
  7. using namespace std;  
  8. long long d[maxn], a[maxn];  
  9. int main() {  
  10.     int n;   
  11.     ios::sync_with_stdio(0);  
  12.     cin >> n;  
  13.     long long p = 0, q = 0;  
  14.     rep(i, 1, n) {  
  15.         cin >> a[i];  
  16.         d[i] = a[i] - a[i - 1];  
  17.         if (i - 1)  
  18.             d[i] >= 0 ? p += d[i] : q += -d[i];  
  19.     }  
  20.     cout << max(p, q) << endl << abs(p - q) + 1;  
  21.     return 0;  
  22. }  
原文地址:https://www.cnblogs.com/TY02/p/11307996.html