IncDec Sequence
题目描述
给定一个长度为n的数列{a1,a2,…,an},每次可以选择一个区间[l,r],使这个区间内的数都加一或者都减一。问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。
输入描述
第一行一个正整数n
接下来n行,每行一个整数,第i+1行的整数表示ai。
输出描述
第一行输出最少操作次数
第二行输出最终能得到多少种结果
样例
样例运行正确并不代表程序没有漏洞,判题服务将使用大量数据对你的程序进行评测。
输入 复制
4
1
1
2
2
输出 复制
1
2
提示
对于100%的数据,n=100000,0≤ai<2147483648。
思路:
首先这道题分为两个小问(1)操作次数(2)结果种类。
(1)要做到所有数都一样,因为访问次数太多防止时间超限,可以想到分差,也就是可以让从d[2]到d[n]都为0。
为什么d[1]可以不为0呢?
因为a[i]=a[i]+d[i-1],a[1]=a[0]+d[1],a[2]=a[3]=…a[n]=a[1]=d[1].所以呀,d[1]多大,这个数列的值就多大,题目要求所有数一样与d[1]的大小没有关系。
怎么求最优解呢
因为这个数列是不确定的,也就是d[i]可能有正有负,而差分的性质就是让d[l]+=c,d[r+l]-=c。我们每次对d[i]进行加或减一的操作,有以下操作方式。
①d[l]++,d[r]-- 2<=l,r<=n 注意:d[l]是一个负值,d[r]是一个正值
②d[1]++,d[l]-- 2<=l<=n 注意d[l]是一个正值,也就是让数列中1~(l-1)的加一个
③d[1]–,d[l]++ 2<=l<=n 注意d[l]是一个负值 ,也就是让数列中1~(l-1)的减一个
④d[l]++,让l以后的都加一个。
⑤d[l]–,让l以后的都减一个。
那么第一种方法正数-1,负数+1,这样相当于一步里作用了两步,比让正数一个个-1和让负数一个个
+1快多了
那么我们可以进行多少种这样的操作呢?
我们可以令差分序列里正数绝对值的总和为p,负数绝对值总和为q
可以进行这样一步顶两步的操作就是min(p,q),因为这种操作正数负数是一一配
对的,当少的那个先用完了,剩下的没有可以配对的了,只能一步步减或一步步加。
所以我们总共要进行的操作就为min(p,q)+abs(p-q),也就是max(p,q)
(2)保证最少次数的前提下,最终得到的数列有多少种?
得到的数列有多少种,其实就是问的b[1]可以有多少种
那么,我们怎么知道b[1]有几种呢?其实就是看看有几种一步步减或一步步加的操作数,因为我们一步步加的时候(假设我们现在的操作对象下标为i),可以这样操作,b[1]-1,b[i]+1一步步减的时候可以这样操作,b[1]+1,b[i]-1(注意,一个差分序列里一步步操作的时候只可能一步步加或一步步减,不可能一步步加和一步步减同时存在)所以说,有几步一步步的操作就有几种情况+1,为什么+1呢,因为这个b[1]本身就有一个值。就算你不对他进行任何操作,它自己也有一种情况。
一加一减(也就是我们所说的一步顶两步的操作)操作数为min(p,q)
那么一步步的操作数就为max(p,q)-min(p,q)=abs(p,q)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int a[N],d[N];
int main()
{
int n;
cin>>n;
int i;
long long z=0,f=0;
for (i=1;i<=n;i++){
cin>>a[i];
if (i==1){
continue;//因为第一个数的分差不用计较,所以输入后直接跳过
}
d[i]=a[i]-a[i-1];//求出分差
if (d[i]>0){
z=z+d[i];
}else if (d[i]<0){
f=f+d[i];
}
}
f=abs(f);
long long p,q;
p=max(z,f);//p相当于是abs(z-f)+min(z,f)这个才是实际的来源
q=abs(z-f)+1;
cout<<p<<endl<<q;
}