acwing 100. 增减序列

题目传送门

题目描述

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

求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。

输入格式

第一行输入正整数 nn。

接下来 nn 行,每行输入一个整数,第 i+1i+1 行的整数代表 aiai。

输出格式

第一行输出最少操作次数。

第二行输出最终能得到多少种结果。

数据范围

0<n≤1050<n≤105,
0≤ai<21474836480≤ai<2147483648

输入样例:

4
1
1
2
2

输出样例:

1
2

差分算法求解

分析

来自《算法竞赛进阶指南》

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;

int a[N];
int b[N];
int n;

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    
    // 求差分数组
    for(int i = 1; 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]; //b[2] -> b[n]所有正数的和
        else         q -= b[i]; //b[2] -> b[n]所有负数的和的绝对值
    }
    
    cout << max(p, q) <<  endl;
    cout << abs(p-q) + 1 << endl;
    return 0;
}

时间复杂度

\(O(n)\)

参考文章

差分解决一段区域同时增加或减少的问题
给区间【L,R】上都加上一个常数c,则b[L] += c , b[R + 1] -=c

  • 求出a的差分序列b,其中b1 = a1,b(i) = a(i) - a(i - 1) (2 <= i <= n)。令b(n + 1) = 0,题目对序列a的操作,相当于每次可以选出b1,b2…b(n + 1)中的任意两个数,一个加1,另外一个减一。目标是把b2,b3,…bn变为全0。最终得到的数列a就是由 n 个 b1 构成的

  • 任选两个数的方法可分为四类
    1、2 <= i , j <=n(优先)
    2、i = 1, 2 <=j <=n
    3、2 <= i <= n , j = n + 1
    4、i = 1, j = n + 1(没有意义)

  • 设b2,b3....bn中正数总和为p,负数总和的绝对值为q。首先以正负数匹配的方式尽量执行1类操作,可执行min(p,q)次。剩余|p - q|个为匹对,每个可以选与b1或b(n + 1)匹配,即执行2 或 3 类操作,共需|p - q|次

  • 综上所诉,最少操作次数为min(p,q) + |p - q|。根据|p - q|次第2、3类操作的选择情况,能产生|p - q| + 1中不同的b1的值,即最终得到的序列a可能有|p - q| + 1 种

作者:小呆呆
链接:https://www.acwing.com/solution/content/5060/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted @ 2022-03-05 16:00  VanHope  阅读(46)  评论(0编辑  收藏  举报