bzoj 3043 IncDec Sequence 差分

题面

题目传送门

解法

模型转化非常精妙

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

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/81980661