P4552题解

Posted cao-yucong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4552题解相关的知识,希望对你有一定的参考价值。

题解P4552

luogu

题目描述

给定一个长度为(n)的数列,每次可以选择区间 ([l,r]), 使得这个区间内的数都加一或者减一。

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

输入输出

第一行一个正整数(n)接下来 (n) 行,每行一个整数,第(i + 1) 行的整数表示(a_i)

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

样例数据

(in)

6
4 8 6 5 10 7

(out)

9
4

题解

因为是区间加减,我们就考虑到使用差分数组。即新定义一个数组(c),使得(c[i] = a[i+1] - a[i])

这样,看到样例,我们就有

[ c = { 4 , -2 , -1 , 5 , -3 } ]

对于区间([l,r]),加1减1的操作对c数组的影响为

[ [l,r]都加一:a[l-1]++,a[r]-- [l,r]都减一:a[l-1]--,a[r]++ ]

(x,y),使得(x)(c)中所有整数之和,(y)(c)中所有负数之和再取反。观察前面的结论,我们可以把操作分成两部分:先是正负抵消,剩下的最后一个数,再单独把这个数递减(或加)到零,所以操作次数就是(max(x,y))

举例

c = { 4 , -2 , -1 , 5 , -3 }
c = { 2 ,  0 , -1 , 5 , -3 }
c = { 1 ,  0 ,  0 , 5 , -3 }
c = { 0 ,  0 ,  0 , 5 , -2 }
c = { 0 ,  0 ,  0 , 0 ,  3 }

第二问。回顾操作两个步骤:“先是正负抵消,剩下的最后一个数,再单独把这个数递减(或加)到零。”那么我们第一步进行完以后一定会得到这个数组:

[ c = {0,0,0,0,0,···,0,0,x-y } ]

(x-y > 0)时,我们可以选择将区间([1,n])都加(x-y),或者将区间([n,n])(x-y),这两种得到的答案就是最终的答案区间,这个区间覆盖的数字个数就是(|x-y| + 1)。所以最终答案为(|x-y| + 1)

同理反之亦然。

为什么不用模拟操作?

因为我们已经直接算出结果来了鸭

AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
long long max(long long a,long long b){return a > b ? a : b;}
long long n;
long long a[100099];
long long c[100099];
int main(){
    cin >> n;
    for(long long i = 1;i <= n; i++) cin >> a[i];
    long long x,y;
    x = y = 0;
    for(long long i = 1;i < n; i++){
        c[i] = a[i+1] - a[i];
        if(c[i] < 0){
            x -= c[i];
        }else{
            y += c[i];
        }
    }

    cout << max(x,y) << endl;

    cout << abs(x-y) + 1 << endl;

    return 0;
}

以上是关于P4552题解的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序代码片段

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器