【ACWing】100. 增减序列

题目地址:

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

给定一个长度为 n n n的数列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,每次可以选择一个区间 [ l , r ] [l,r] [l,r],使下标在这个区间内的数都加一或者都减一。求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。

输入格式:
第一行输入正整数 n n n
接下来 n n n行,每行输入一个整数,第 i + 1 i+1 i+1行的整数代表 a i a_i ai

输出格式:
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。

数据范围:
0 < n ≤ 1 0 5 0<n≤10^5 0<n105
0 ≤ a i < 2147483648 0≤a_i<2147483648 0ai<2147483648

首先求一下 a a a的差分数组 b b b,即 b i = a i − a i − 1 b_i=a_i-a_{i-1} bi=aiai1。那么将某个区间 [ l , r ] [l,r] [l,r]的数都加 1 1 1等价于将 b [ l ] b[l] b[l] 1 1 1并且将 b [ r + 1 ] b[r+1] b[r+1] 1 1 1;而如果将某个区间 [ l , r ] [l,r] [l,r]的数都减 1 1 1等价于将 b [ l ] b[l] b[l] 1 1 1并且将 b [ r + 1 ] b[r+1] b[r+1] 1 1 1。我们首先考虑最少操作次数应该是多少。如果最后真的让所有的数都变成了一样,那么 b b b会满足 b 2 = b 3 = . . . = b n = 0 b_2=b_3=...=b_n=0 b2=b3=...=bn=0。我们不妨设 ∑ b i > 0 b i = p , − ∑ b i < 0 b i = q \sum_{b_i>0}b_i=p,-\sum_{b_i<0}b_i=q bi>0bi=p,bi<0bi=q,如果 p > q p>q p>q,那么我们可以先每次都从 b 2 ∼ n + 1 b_{2\sim n+1} b2n+1中选取一正一负,正的减 1 1 1,负的加 1 1 1,先把 b b b中的负数全消灭掉,接下来如果还有非零的数,不妨设这些数都是负的,这样可以通过调整 b 1 b_1 b1 b n + 1 b_{n+1} bn+1的办法将这些负数也都变为 0 0 0,最终就达到了将 b b b调整为满足条件的序列的结果。这种操作方式的次数为 min ⁡ { p , q } + ∣ p − q ∣ \min\{p,q\}+|p-q| min{p,q}+pq,并且一定是次数最少的,因为要将 b 2 ∼ n b_{2\sim n} b2n都变为 0 0 0的话,正负抵消一定是最快的,抵消不掉的则需要借助 b 1 b_1 b1。最后得到的数列的个数即取决于 b 1 b_1 b1变成了什么,因为正负抵消掉之后的调整过程,可以调整 b 1 b_1 b1也可以调整 b n + 1 b_{n+1} bn+1,所以一共有 ∣ p − q ∣ + 1 |p-q|+1 pq+1种可能。代码如下:

#include <iostream>
using namespace std;
using ll = long long;

const int N = 1e5 + 10;
int a[N];

int main() {
  int n;
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  for (int i = n; i > 1; i--) a[i] -= a[i - 1];

  ll pos = 0, neg = 0;
  for (int i = 2; i <= n; i++)
    if (a[i] > 0) pos += a[i];
    else neg -= a[i];
  
  printf("%lld\n", min(pos, neg) + abs(pos - neg));
  printf("%lld\n", abs(pos - neg) + 1);
}

时空复杂度 O ( n ) O(n) O(n)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值