欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

bzoj 3043 IncDec Sequence 差分

程序员文章站 2022-07-12 17:58:14
...

题面

题目传送门

解法

模型转化非常精妙

  • 将区间[l,r]+v,可以变成在差分数组第l个位置+v,在第r+1个位置v
  • 那么,我们可以考虑将问题转化一下,变成给定n1个数,每一次可以将某一个数1+1,另一个数可以不操作,否则做和第一个数相反的操作。问至少多少次这个数列变成全0
  • 考虑一个贪心的解法,显然是每一次对一个负数+1,对一个正数1,如果不存在对应的正数或负数,直接自己加或减变成0
  • 显然,这个方法能保证答案是最小的,而且每一次操作都可以对应一个区间的加减
  • 那么,我们设a=i=2n|di| (di<0),b=i=2ndi (di>0)
  • 根据上述贪心算法,显然最少的操作次数就是max(a,b)
  • 那么,我们再考虑一下最后会出现多少种不同的序列。显然,在正数或负数全部被消去后,剩下的可以随意安排给dn+1或者d1,所以方案数就是|ab|+1
  • 时间复杂度:O(n)

代码

#include <bits/stdc++.h>
#define int long long
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int a[N], b[N];
main() {
    int n; read(n);
    for (int i = 1; i <= n; i++) read(a[i]);
    for (int i = 2; i <= n; i++) b[i] = a[i] - a[i - 1];
    int s1 = 0, s2 = 0;
    for (int i = 2; i <= n; i++)
        if (b[i] > 0) s1 += b[i]; else s2 -= b[i];
    cout << max(s1, s2) << "\n" << abs(s1 - s2) + 1 << "\n";
    return 0;
}