题目链接
题目描述:
给定一个长度为 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
数组内所有元素值相同,故最后差分数组b
中b[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]
,而l
和r
范围都是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
。然后会面临三种情况。
-
sum1
和sum2
的绝对值相等,b[2]~b[n]
内正负元素刚好抵消,此时达到目标,操作次数为sum1
或sum2
的绝对值,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;
}