标签:要求 else namespace string def += lin ble 复杂度
题目
题目
题解
首先,我们确定几个性质:
- 答案要正确,必须要保证操作次数最小(这不是废话吗(╯‵□′)╯︵┻━┻)。
- 对于同一个区间而言,只能存在\(+\)或\(-\)的操作,否则是浪费。
- 对于一个\(+\)操作而言,不能有和它相接的\(+\)操作,\(-\)操作也是,否则可以合并并造成更小的次数,如:\(1-3\)和\(3-5\)合并成\(1-5\)
- 如果存在\(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\),因此这个如果不成立。
- 不可能有\(+[1,n]\)这种\(SB\)的操作。
- 在遵循上述操作的情况下,对于\(+[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]\)。
- 操作序列顺序不影响结果。
- 如果两种操作序列的结果一样(不要求次数相同),这两种一定可以通过非反操作的操作互相转化。(这个我是真的不会证,只能说是它本来就是\(+-\)具有的性质吧。)
- 以\(j\)为结尾操作都是同号的,异号可以合并。
- [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