Luogu P4552 [Poetize6] IncDec Sequence|差分

题目链接
题目大意:
给定一个长度为 (n) 的数列 { (a_1) , (a_2),(cdots),(a_n)} ,每次可以选择一个区间([l,r]),使这个区间内的数都加 (1) 或者都减 (1)

请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

题目思路:

以下的解法可能不是最优,敬请留意。

这种序列题常常会用到前缀和/差分进行求解。由于本题要求最后数字相同,可以想到差分后为数组为0。考虑差分。

差分后数组中有正数,负数和0,题目转化为需使不为0的数变为0的操作数。当然,差分数组的第1和第n+1项除外,因为其跳跃出边界。

现在,题目中的加1操作变为将左端点(+1),右端点(-1) ,减1操作则恰好相反。

那么,要使操作最小,我们可以猜测应该使操作两个影响点应尽量在非(1)(n+1)的位置上,也就是都有效。剩余的则只能与(1)(n+1)项配合完成。这个操作显然可以模拟。

当然,这个解法并不算优,如需更优欢迎移步Luogu

第一问可以解决了,现在考虑第二问:

我们将第一个数作为指标,考虑第一个数有多少变化可能。第一个数变化当且仅当操作涉及到第一个数,也就是无法与中间配对的操作(这些操作可以与(1)(n+1)配对,是等价,可互换的)。再加上原来的一个即可。

上代码

#include<bits/stdc++.h>
using namespace std;
int n,a[100100],c[100100],qp[100100],qf[100100];
long long ans,sum;
int main()
{
	cin>>n;
	int hp=1,tp=0,hf=1,tf=0;
	for (int i=1;i<=n;i++)
	{
		cin>>a[i];
		c[i]=a[i]-a[i-1];
		if (i==1) continue;
		if (c[i]>0) qp[++tp]=i;
		if (c[i]<0) qf[++tf]=i;
	}
	sum=1;
	while (hp<=tp||hf<=tf)
	{
		int kp=qp[hp],kf=qf[hf];
		if (hp>tp) c[kp]=2147483647;
		if (hf>tf) c[kf]=-2147483647;//不能与2~n配对,那么有可能影响1
		int v=min(c[kp],-c[kf]);
		if (c[kp]==2147483647) sum+=-c[kf];
		if (c[kf]==-2147483647) sum+=c[kp];
		ans+=v;
                 //可配对情况
		if (c[kp]>-c[kf])
		{
			c[kp]-=v;c[kf]+=v;
			hf++;
		}
		else
		{
			if (c[kp]==-c[kf])
			{
				c[kp]-=v;c[kf]+=v;
				hf++;hp++;
			}
			else
			{
				c[kp]-=v;c[kf]+=v;
				hp++;
			}
		}
	}
	cout<<ans<<endl<<sum;
	return 0;
}
原文地址:https://www.cnblogs.com/fmj123/p/14002506.html