Acwing 100. 增减序列

差分,推导

计数类的区间修改一定要考虑用差分

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

首先确定差分的方法,然后你会发现:当数组中所有元素相等时,\(b_2 = b_3 = ... = b_n\)

在差分数组中,若要使区间 \([l,r]\)\(k\),则\(b_{l-1} + k , \ b_r - k\)

这样题目就变成了:给你一个数组\(b\),每次改变数组中的一对元素(任意一对),只能加一或减一(另一个数操作与之相反),使\(b_2 = b_3 = ... = b_n\)

*推导

以下的数组都是指差分数组\(b\),都在\(b\)中实现。

因为要一加一减,那么就需要正负数配合完成。但是有时候会剩下一些数,那么可以通过更改它和\(b_1或b_{n+1}\)来实现,也就是最后一定可以达到理想状态

\(p等于所有正数的绝对值,q等于所有负数的绝对值\),其中可以同时消去的操作有\(min(p,q)\),剩下的操作有\(|p-q|\),那么答案其实就是\(min(p,q)+|p-q|=max(p,q)\)

那么有多少种情况呢?,其实同时消去的操作已经最简,并且无法消去,那么只能在剩下的操作中下手,若剩下的数字总数(其实就是操作次数)为\(z\),每次操作两种选择,但是无论怎么修改\(b_{n+1}\),对原数组的值都没有影响,但\(b_1\)是会受到影响的,所以操作之后,\(b_1\)的取值范围是$ b_1 ≤ b_1' ≤ b_1 + z$,那么可取的值有 \(z + 1\) 种,也就是\(|p-q| + 1\)

问题迎刃而解!当然代码很简单。

AC code

#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int a[N], b[N];

int main()
{
    int n; cin >> n;
    for(int i = 1; i <= n; i ++ ) cin >> a[i];
    b[1] = a[1];
    for(int i = 2; i <= n; i ++ ) b[i] = a[i] - a[i - 1];
    
    ll p = 0, q = 0; // 正数,负数
    for(int i = 2; i <= n; i ++ )
    {
        if(b[i] > 0) p += b[i];
        if(b[i] < 0) q += -b[i];
    }
    cout << max(p, q) << endl << abs(p - q) + 1;
    
    return 0;
}
posted @ 2023-01-21 13:02  Sankano  阅读(17)  评论(0编辑  收藏  举报