码迷,mamicode.com
首页 > 其他好文 > 详细

IncDec序列

时间:2020-07-29 15:33:27      阅读:69      评论:0      收藏:0      [点我收藏+]

标签:要求   else   namespace   string   def   +=   lin   ble   复杂度   

题目

题目

题解

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

  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\n",mymax(q,p));
	printf("%lld\n",zabs(q-p)+1);
	return  0;
}

IncDec序列

标签:要求   else   namespace   string   def   +=   lin   ble   复杂度   

原文地址:https://www.cnblogs.com/zhangjianjunab/p/13396554.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!