题目描述
给定一个长度为n的数列a1,a2,……,an,每次可以选择一个区间[l,r],使这个区间内的数都加1或者都减1。
请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。
输入输出格式
输入格式:
第一行一个正整数n
接下来n行,每行一个整数,第i+1行的整数表示ai
输出格式:
第一行输出最少操作次数
第二行输出最终能得到多少种结果
输入输出样例
输入样例#1:
4
1
1
2
2
输出样例#1:
1
2
说明
对于100%的数据,n≤100000,0≤ai≤2147483648。
写的好乱,看例子好理解点
求差分:相邻两项做差
数组 a r r [ 1 ] , a r r [ 2 ] … a r r [ n ] arr[1],arr[2]…arr[n] arr[1],arr[2]…arr[n]
差分 n u m [ 1 ] = a r r [ 1 ] , n u m [ i ] = a r r [ i ] − a r r [ i − 1 ] num[1]=arr[1],num[i]=arr[i]-arr[i-1] num[1]=arr[1],num[i]=arr[i]−arr[i−1]
有 a r r [ i ] = n u m [ 1 ] + n u m [ 2 ] + … + n u m [ i ] arr[i]=num[1]+num[2]+…+num[i] arr[i]=num[1]+num[2]+…+num[i]
数组值为差分前缀和
本题要求数列所有的数一样,也就要求是差分数组在 [ 2 , n ] [2,n] [2,n] 都为0
以下所有对差分的操作都在 [ 2 , n ] [2,n] [2,n]
如果对数组 [ i , j ] [i,j] [i,j] 范围内加减同一数字 k k k,可以转化为对差分数组的改变 n u m [ i ] + = k , n u m [ j + 1 ] − = k num[i]+=k,num[j+1]-=k num[i]+=k,num[j+1]−=k (对范围的改变转化为对端点的改变)
(1)当 ± k \pm k ±k成对出现时表示在区间内 ± k \pm k ±k
(2)当 ± k \pm k ±k单独出现时表示在自身即后面所有元素内 ± k \pm k ±k (其实是操作区间[i,n])
也就是 n u m [ i ] = k num[i]=k num[i]=k代表 i i i后所有的数与前面所有数相差k
对于数据
i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
数组 | 1 | 1 | 2 | 2 | 1 | 3 |
差分 | 1 | 0 | 1 | 0 | -1 | 2 |
差分数组中有一对 ± 1 \pm1 ±1 还有一个单独的2
先把 ± 1 \pm1 ±1 消除掉,也就是在区间 [ 3 , 4 ] [3,4] [3,4] 内减1, n u m [ 3 ] − = 1 , n u m [ 5 ] + = 1 num[3]-=1,num[5]+=1 num[3]−=1,num[5]+=1
i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
数组 | 1 | 1 | 1 | 1 | 1 | 3 |
差分 | 1 | 0 | 0 | 0 | 0 | 2 |
对于2的操作有三种
1 : a r r [ 1 , 5 ] 1:arr[1,5] 1:arr[1,5] 加一 , n u m [ 1 ] + = 1 , n u m [ 6 ] − = 1 ,num[1]+=1,num[6]-=1 ,num[1]+=1,num[6]−=1 ,操作两次,对应 ( 1 ) (1) (1)
i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
数组 | 3 | 3 | 3 | 3 | 3 | 3 |
差分 | 3 | 0 | 0 | 0 | 0 | 0 |
2 : a r r [ 6 ] 2:arr[6] 2:arr[6] 自己减一 , n u m [ 6 ] − = 1 ,num[6]-=1 ,num[6]−=1 ,操作两次,对应 ( 2 ) (2) (2)
i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
数组 | 1 | 1 | 1 | 1 | 1 | 1 |
差分 | 1 | 0 | 0 | 0 | 0 | 0 |
3 : a r r [ 1 , 5 ] 3:arr[1,5] 3:arr[1,5] 加一 , n u m [ 1 ] + = 1 , n u m [ 6 ] − = 1 ,num[1]+=1,num[6]-=1 ,num[1]+=1,num[6]−=1 ,然后 a r r [ 6 ] arr[6] arr[6] 自己减一
i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
数组 | 2 | 2 | 2 | 2 | 2 | 2 |
差分 | 2 | 0 | 0 | 0 | 0 | 0 |
设差分数组中正数和ans1,负数和ans2
在将除 n u m [ 1 ] num[1] num[1] 外全部变为0的过程中,先把成对的 ± 1 \pm 1 ±1 消除,操作数为 m i n ( a n s 1 , a n s 2 ) min(ans1,ans2) min(ans1,ans2) 然后消除单独的正负数,操作数为 a b s ( a n s 1 − a n s 2 ) abs(ans1-ans2) abs(ans1−ans2) ,总的操作数就是 m a x ( a n s 1 , a n s 2 ) max(ans1,ans2) max(ans1,ans2)
最后答案的种类就是 n u m [ 1 ] num[1] num[1] 的所有可能取值,从例子中看出就是单独的正负数的个数加1,因为 n u m [ 1 ] num[1] num[1] 的操作可以是(假如剩下的是正数,值为k) + 0 , + 1 , + 2 , . . . , + k +0,+1,+2,...,+k +0,+1,+2,...,+k 一共 k + 1 k+1 k+1 种
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
static const auto io_sync_off = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return nullptr;
}();
using ll=long long;
const int maxn=100005;
ll arr[maxn],nums[maxn];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;++i)
cin>>arr[i];
for(int i=n-1;i>0;--i)
arr[i]=arr[i]-arr[i-1];//差分
ll ans1=0,ans2=0;
for(int i=1;i<n;++i)
if(arr[i]>0)
ans1+=arr[i];
else
ans2+=abs(arr[i]);
cout<<max(ans1,ans2)<<endl;
cout<<abs(ans1-ans2)+1;
return 0;
}