IncDec序列

题目

题目

题解

首先,我们确定几个性质:

  1. 答案要正确,必须要保证操作次数最小(这不是废话吗(╯‵□′)╯︵┻━┻)。
  2. 对于同一个区间而言,只能存在(+)(-)的操作,否则是浪费。
  3. 对于一个(+)操作而言,不能有和它相接的(+)操作,(-)操作也是,否则可以合并并造成更小的次数,如:(1-3)(3-5)合并成(1-5)
  4. 如果存在(k)个加/减号操作:(+[i_1,j_1],+[i_2,j_2],...,+[i_k,j_k])且满足(i_1=1,j_k=n,i_t<=j_{t+1}),那么这个可以变成:(+[1,n],+[j_1,i_2]...,+[j_{k-1},i_{k}]),然后(+[1,n])去掉,次数减一,因此不存在这种情况,当然,通过这个可以推广出如果有(k)个加号操作:(+[i_1,j_1],+[i_2,j_2],...,+[i_k,j_k])且满足(i_t<=j_{t+1})那么就可以变成:(+[i_1,j_k],+[j_1,i_2]...,+[j_{k-1},i_{k}]),如果这时恰好有一个(-[i_1,j_k])就可以刚好抵消了,次数(-1),因此这个如果不成立。
  5. 不可能有(+[1,n])这种(SB)的操作。
  6. 在遵循上述操作的情况下,对于(+[i,j])操作而言,我们可以分成两个操作:(-[1,i-1])(-[j+1,n]),我们称之为反操作,为什么对?因为这个区间(+1)不就意味着其它区间(-1)吗?当然,如果(i≠1,j≠n),那么就会产生多一次操作次数(因为(i=1)(j=n)话,左边或者右边是没有数字的,所以只是对除了([i,j])以外的另外一个区间作反操作罢了,次数不会增加),当然你如果头贴硬生生去消除其中一个操作倒是可以,但是前提是得有一个([?,n])或者([1,?])的操作才可以消掉。
    也许你不是很理解,我们举个栗子:
    对于操作(+[2,3])(n=6),那么反操作就是(-[1,1],-[4,6]),就会增加两个操作,而且如果存在(+[4,?])(-[4,6])相互抵消的话,那么(+[2,3])(+[4,?])是可以合并的,那你又会问那如果是(+[3,6])和它抵消呢?没错,这样就可以消掉了,但是就会产生一个(+[3,4])的操作,所以消掉了其中一个反操作的影响,但是别想利用这个消掉原本的操作来减少次数。但是不是有(+[4,6])就可以消掉了吗,但是([4,6])和[2,3]可以合并。因此可以说明存在一个([?,6])才可以消除影响(当然,对于(?>4)的时候,就是(-[4,6])(+[?,6])吞并变成(+[4,?]))。
    但是同时又不可能同时把两个同时消掉影响,不然以性质4,我们在原来的区间操作就可以让次数(-1)了(这也是为什么(i=1)(j=n)的反操作无法消掉的原因)。
    当然通过这个,我们就可以得到,每一个([1,?])或者([?,n])就可以让数字(+1)或者(-1)(自己取反操作肯定可以),当然,这个是对应的,消耗掉一个([1,?])就可以产生一个反符号的([?,n])
  7. 操作序列顺序不影响结果。
  8. 如果两种操作序列的结果一样(不要求次数相同),这两种一定可以通过非反操作的操作互相转化。(这个我是真的不会证,只能说是它本来就是(+-)具有的性质吧。)
  9. (j)为结尾操作都是同号的,异号可以合并。
  10. [1,?]和[?,n]数量的差是固定的,对于固定的([?,n])而言,在满足性质1的情况下,操作序列的结果不会有任何改变,不管操作序列怎么变(因为中间的操作要取反操作必须有[1,?]和[?,n]的转化),而且([1,?])([?,n])的操作符号绝对是反的,正的可以合并成一个操作,如:(+[1,3],+[5,n])可以合并成(-[4,4])一样,就不是最少操作次数了。

这些性质我们先不管它,先看第一问,最少操作次数?我们要搞清楚一件事情,区间加减其实就是差分数组的单点修改,那我们只需要把数组差分一下,然后对于成对(1)(-1)匹配一下,那么对于单独的(1)或者是(-1),我们应该如何处理呢?仔细观察,我们发现([i,n])的操作,只会产生一个(1)或者一个(-1),与之成对的(-1/1)是在(n+1)的位置的,我们就不管它了,所以对于剩下的(1)或者(-1),我们就认为它是做了([i,n])的加减。

这样为什么是最优秀最少的呢?因为一次加或者减最多只能消除掉一对(1,-1),剩下的(1/-1)不就是需要一个一个操作慢慢消了吗?所以这种方式构造出来的就是最优解,而且这样子的构造还方便了我们一个事情,就是我们得到了有多少个([?,n]),根据性质(9),以(n)结尾的操作都是同号的,而且这样构造不会出现([1,?])的情况(因为差分是以第一个数字为标准),现在以(+[?,n])为例,如果以(n)结尾的操作都是(+)操作,那么我们可以去掉一个(+)号操作然后产生一个(-[1,?])同时让结果(-1),所以有多少个加号,就多了多少种结果(负号同理),所以答案就是(abs(差分[n])+1)就是在最小操作次数下面有多少种答案啦。

总是是讲完了,虽然我感觉讲得很乱,但至少把思路讲了。
大概就是证明了一个([?,n])的操作就可以让结果变(1)且操作次数不变的性质是对的。

代码

时间复杂度:(O(n))

#include<cstdio>
#include<cstring>
#define  N  110000
using  namespace  std;
typedef  long  long  LL;
LL  a[N],b[N],n;
inline  LL  mymax(LL  x,LL  y){return  x>y?x:y;}
inline  LL  zabs(LL  x){return  x<0?-x:x;}
int  main()
{
	scanf("%lld",&n);
	for(int  i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int  i=1;i<n;i++)b[i]=a[i+1]-a[i];
	LL  q=0,p=0;
	for(int  i=1;i<n;i++)
	{
		if(b[i]>0)q+=b[i];
		else  p+=-b[i];
	}
	printf("%lld
",mymax(q,p));
	printf("%lld
",zabs(q-p)+1);
	return  0;
}
原文地址:https://www.cnblogs.com/zhangjianjunab/p/13396554.html