acwing-100.增减序列

题目链接


题目描述:

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

输入格式:

第一行输入正整数 n

接下来 n 行,每行输入一个整数,第 i+1 行的整数代表 ai

输出格式:

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

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

数据范围:

0<n≤1050≤ai<21474836480

输入样例:

4
1
1
2
2

输出样例:

1
2


分析:

要对数组若干个子区间整体加1或者减1,考虑差分。而差分数组一定始终满足如下关系:

b[1]=a[1]b[2]=a[2]-a[1]b[3]=a[3]-a[2] ......b[n]=a[n]-a[n-1]

最终要使得a数组内所有元素值相同,故最后差分数组bb[2]~b[n]都为0。即求:

  • 最少操作多少次能使得b[2]~b[n]都为0

  • 在最少操作次数的前提下,b[1]有几种取值情况

而每对a数组进行一次区间[l,r]整体加减1操作时,b数组内有两个元素发送改变,b[l]加上1,b[r+1]减去1

由于每次操作改变的两个元素是b[l]b[r+1],而lr范围都是1~n,所以每次都是从b[1]~b[n+1]任意挑选两个元素,一个加上1,一个减去1。

要求经过最少的操作次数 使得b[2]~b[n]都为0,显然应该先挑选b[2]~b[n]内的元素使得正数元素不断减1,负数元素不断加1。我们设b[2]~b[n]内正数元素总和为sum1负数元素总和为sum2 。然后会面临三种情况。

  • sum1sum2的绝对值相等,b[2]~b[n]内正负元素刚好抵消,此时达到目标,操作次数为sum1sum2的绝对值,b[1]始终没变,只有一种取值情况。

  • sum1的绝对值大于sum2的绝对值,b[2]~b[n]内只剩下正数元素,这时候每次操作应该让b[2]~b[n]内剩余的正数元素减1,让b[1]或者b[n]加1,每次选择b[1]还是b[n]加1就会造成b[1]的最终值不同。最终要使得b[2]~b[n]内元素全为0则需要操作sum1的绝对值的操作次数,b[1]有1+|sum1|-|sum2|种取值情况。

  • sum1的绝对值小于sum2的绝对值,b[2]~b[n]内只剩下负数元素,这时候每次操作应该让b[2]~b[n]内剩余的负数元素加1,让b[1]或者b[n]减1,每次选择b[1]还是b[n]减1就会造成b[1]的最终值不同。最终要使得b[2]~b[n]内元素全为0则需要操作sum2的绝对值的操作次数,b[1]1+|sum2|-|sum1|种取值情况。


实现代码如下:


#include<cstdio>
#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<vector>
#include<bitset>
#include<deque>
#include<cctype>
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    
    long long sum1=0,sum2=0,sum3;
    long long cnt1,cnt2;//cnt1存操作次数,cnt2存结果种数
    //构造差分数组
    for(int i=n;i>=1;i--)
    {
        a[i]=a[i]-a[i-1];
    }
    //求b[2]~b[n]内正数和 与 负数和
    for(int i=2;i<=n;i++)
    {
        if(a[i]>0) sum1+=a[i];//sum1存正数
        else if(a[i]<0) sum2+=a[i];//sum2存负数
    }
    sum3=sum1+sum2;//存差值
    if(sum3==0)//正数和负数一样大
    {
        cnt1=sum1;
        cnt2=1;
    }
    else if(sum3>0)//正数大
    {
        cnt1=sum1;
        cnt2=1+sum3;
    }
    else//负数大
    {
        cnt1=-sum2;
        cnt2=1-sum3;
    }
    cout<<cnt1<<endl;
    cout<<cnt2<<endl;
}


  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值