ACWING 100 IncDec序列

https://www.acwing.com/problem/content/102/

题意

对于给定的数列a,求通过区间加和区间减使其中所有数都一直的最小步数,并给出最小步数的所有方案。

思路

考虑差分数组操作。可以将这些操作分为四种,这里以i和j指代 l e f t left left r i g h t + 1 right+1 right+1

  1. i > 1 , j < = n i>1,j<=n i>1,j<=n 即对一区间进行加减操作,且这一区间并非前缀或后缀。
  2. i = 1 , j < = n i=1,j<=n i=1,j<=n 对数列的长为 j j j的前缀进行操作
  3. i > 1 , j = n + 1 i>1,j=n+1 i>1,j=n+1 对数列的长为 j j j的后缀进行操作
  4. i = 1 , j = n + 1 i=1,j=n+1 i=1,j=n+1 对整条序列进行操作

不难看出,最后一类操作并无意义,所以我们仅考虑前三种即可。
把数列a中所有数变得相同,这一目标可以转化为将数列a的差分数组 b b b变成如下形式
a [ i ] , 0 , 0 , 0 , 0 , 0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ a[i],0,0,0,0,0······ a[i],0,0,0,0,0⋅⋅⋅⋅⋅⋅
这也就是说,数列a中的每一个元素都与第1个元素相同,因此差分为0。

考虑转化后的这一目标,我们可以使用两个变量 p o s i t i v e positive positive n e g a t i v e negative negative记录差分数组 b b b中正数的绝对值之和,以及负数的绝对值之和。并且在操作过程中,将正数向0减,将负数向0加。

考虑差分数组的操作,即 b [ l e f t ] + + , b [ r i g h t + 1 ] − − b[left]++,b[right+1]-- b[left]++,b[right+1],可以将“将正数向0减”和“将负数向0加”的过程配对进行。

这也就是说,我们希望优先进行第1种操作,在数列a中选取异号的 a [ i ] a[i] a[i] a [ j ] a[j] a[j],使对正数的减和负数的加能够在一次操作中完成。

由此,我们推导出最小次数的公式
m i n ( p o s i t i v e , n e g a t i v e ) + a b s ( p o s i t i v e − n e g a t i v e ) min(positive,negative)+abs(positive-negative) min(positive,negative)+abs(positivenegative)

m i n ( p o s i t i v e , n e g a t i v e ) min(positive,negative) min(positive,negative)代表将加和减配对进行的过程。在这之后,差分数组 b b b中除第一位之外应只剩下正数或者负数。

a b s ( p o s i t i v e − n e g a t i v e ) abs(positive-negative) abs(positivenegative)代表将剩下的正数(或负数)归零处理。

接下来,我们考虑方案数的统计。

由于在之前的操作中,我们已经使得在差分数组b中除 b [ 1 ] b[1] b[1]之外只有正数或者负数,所以第1种操作将加减配对进行不再可行。我们转而考虑第2种和第3种操作。

第2或3种操作,实质上对应着与 b 1 b_1 b1 b n + 1 b_{n+1} bn+1配对。而 a b s ( p o s i t i v e − n e g a t i v e ) abs(positive-negative) abs(positivenegative)次操作,对应着 a b s ( p o s i t i v e − n e g a t i v e ) + 1 abs(positive-negative)+1 abs(positivenegative)+1 b 1 b_1 b1的取值。这也就是说,从不与 b 1 b_1 b1配对(配对0次)到每次都与 b 1 b_1 b1配对,共有 a b s ( p o s i t i v e − n e g a t i v e ) abs(positive-negative) abs(positivenegative)种情况。

代码中也可不必记录原数组而直接在原数组上进行差分,但为了保证与题解的良好对应,将其保留之。

#include <iostream>
#include <algorithm>
#include <cmath>
typedef long long ll;
using namespace std;
const int N = 100010;
ll a[N];
ll b[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        b[i] = a[i] - a[i - 1];
    }
    ll positive = 0;
    ll negative = 0;
    for (int i = 2; i <= n; i++)
    {
        if (b[i] > 0)
            positive += b[i];
        else
            negative -= b[i];
    }
    cout << min(positive, negative) + abs(positive - negative) << endl;
    cout << abs(positive - negative) + 1 << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值