题解 P4552 【[Poetize6] IncDec Sequence】

mot1ve

2020-05-12 16:09:36

Solution

一个小时才学会差分= =太弱了,分享一下我学习差分的心得。非常详细 虽然很长但 看完保证明白,不明白可以私信问。 (本篇题解我们的数组的下标从1开始)差分的概念是b[1]=a[1],b[i]=a[i]-a[i-1], 简单来说就是两个数的差。b[1]一定是等于a[1]的,因为b[1]=a[1]-a[0],而 a[0]=0,所以b[1]=a[1]。 了解了概念,我们看一下差分和原序列之间有什么关系 把序列a的区间[l,r]+d(或者说a[l]+d,a[l+1]+d,a[l+2]+d+....+a[r]+d的 话,那么这个序列a的差分序列b的变化就为b[l]+d,b[r+1]-d。为什么呢?举个例子 原序列a:1 3 4 2 1,其差分序列b:1 2 1 -2 -1 把区间[2,4]+2,得到的序列a应该是1 5 6 4 1 再看差分序列b,根据我们上面说的公式,a[2,4]+2应该等于b[2]+2,b[5]-2; 差分序列b变为:1 4 1 -2 -3 到底是不是这样呢?我们根据差分的概念倒推回去看看 由于b[1]=a[1],且b[1]=1,所以a[1]=1; b[2]=4,则a[2]-a[1]=b[2]=4;由于a[1]=1,得出a[2]=5; b[3]=1,则a[3]-a[2]=1,由于a[2]=5,得出a[3]=6; b[4]=-2,则a[4]-a[3]=-2,由于a[3]=6,得出a[4]=4; b[5]=-3,则a[5]-a[4]=-3,由于a[4]=4,得出a[5]=1; 由差分序列倒推回来得到的原序列是1 5 6 4 1,完全符合我们之前得到的,说明这 个公式是正确的。 直观点说,原序列1 3 4 2 1,把区间[2,4]+2,得到的序列a是1 5 6 4 1 可以发现,a[2,4]中的差是不变的,因为他们同时加了一个数,变化的是a[l-1]和 a[l]之间的差以及a[r]和a[r+1]之间的差,这样一来,就很好推出这个差分序列公式 下面是这个公式的延伸 如果a[l,r]+1,则b[l]+1,b[r+1]-1; 如果a[l,r]-1,则b[l]-1,b[r+1]+1; 如果a[l,n]+1(l <= n - 1),则b[l]+1,其余不变,因为b[n+1]已越界无意义 如果a[l,n]-1(l <= n - 1),则b[l]-1,其余不变,因为b[n+1]已越界无意义 下面看一下这个题该怎么做 要使得序列的数全部相等,其实就是让他们之间的差全为0,也就是差分序列的除了第 一项每一项都是0,为什么除了第一项呢,因为b[1]=a[1]-a[0],而a[1]是开头的数 我们把问题转化成了让差分序列除第一项全等于0之后,继续思考 由于题目要求最少的步骤,我们可以考虑,如果差分序列里有一个正数和一个负数 (出现的顺序无所谓),那么我们优先对这个正数和负数进行操作,为什么呢?因为 我们有以下两个公式 如果a[l,r]+1,则b[l]+1,b[r+1]-1 如果a[l,r]-1,则b[l]-1,b[r+1]+1 正数-1,负数+1,这样相当于一步里作用了两步,比让正数一个个-1和让负数一个个 +1快多了 那么我们可以进行多少种这样的操作呢? 我们可以令差分序列里正数绝对值的总和为p,负数绝对值总和为q 可以进行这样一步顶两步的操作就是min(p,q),因为这种操作正数负数是一一配 对的,当少的那个先用完了,剩下的没有可以配对的了,只能一步步减或一步步加。 所以我们总共要进行的操作就为min(p,q)+abs(p-q),也就是max(p,q) 第一问完成,看第二问 保证最少次数的前提下,最终得到的数列有多少种? 得到的数列有多少种,其实就是问的b[1]可以有多少种 我们上述所有操作是与b[1]无关的,因为我们的目标是让除了b[1]以外的项变0,所 以我们上述的操作没有考虑到b[1],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<iostream> #include<cstdio> #include<algorithm> using namespace std; typedef long long LL; LL n,c,p,q,a[100010]; int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } for(int i=2;i<=n;i++) { c=a[i]-a[i-1]; if(c>0) { p+=c; } else q-=c; } LL ans1=max(p,q); LL ans2=abs(p-q)+1; cout<<ans1<<endl<<ans2; return 0; }