BZOJ系列3043《IncDec Sequence》题解

Description

给定一个长度为n的数列{a1,a2...an},每次可以选择一个区间[l,r],使这个区间内的数都加一或者都减一。
问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

Input

第一行一个正整数n 
接下来n行,每行一个整数,第i+1行的整数表示ai。

Output

第一行输出最少操作次数
第二行输出最终能得到多少种结果

Sample Input

4
1
1
2
2

Sample Output


1
2

HINT

对于100%的数据,n=100000,0<=ai<2147483648

Source


看完题之后惊了个呆,前不久刚刚做过,就无耻的粘代码了!

题解:

相邻两项做差,也就是数列的差分,这样就把区间修改问题改为单个元素修改问题。
并且做差之后,问题也基本转换成功。
对于带有“将一段区间内的每个数全部加上某个值”这种操作的题目,通常考虑差分原数列以简化情况,

将对一段区间的操作转化为对某两个特定数的操作。
我们定义d_1 = a_1, d_i = a_i - a_{i-1} ( 2 ≤ i ≤ n ), d_{n+1} = 0

(事实上,稍后我们会看到d_1和d_{n+1}的值并不重要),

可以发现,原题中的“将[l,r]内的数都加一或都减一”将对应“将d_l + 1,将d_{r+1} - 1”(或反之)的操作。

显然,题目中要求的a数列中的所有数全部相等的条件等同于使d_i = 0 ( 2 ≤ i ≤ n ),最后数列中的数即为d_1,

而题目中的操作允许我们把d数列中的某个数+1,某个数-1。

要将d数列中第二项至第n项全部变为0并使操作次数最少,首先我们将每个负数和每个正数配对执行操作,

设d数列中第2至第n项所有正数分别求和得到的值为p,负数分别求和得到的值的*绝对值*为q,

这一步的操作次数即为min{p,q}。此时还剩余和的绝对值为abs(p-q)的数没有变为0,

每次操作我们可以将其与d_1或d_{n+1}配对进行操作,操作次数为abs(p-q),容易看出,

最终d_1的可能取值有abs(p-q)+1种。因此,第一问的答案即为max{p,q},第二问的答案即为abs(p-q)+1。


标准题解如上。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int N,a[100010],f[100010];
long long p=0,q=0,ans1,ans2;
long long abs1(long long x)
{
	return x>0?x:(-x);
}
long long max1(long long x,long long y)
{
	return x>y?x:y;
}
void init()
{
	scanf("%d",&N);
	for(int i=1;i<=N;i++)
	{
		scanf("%d",&a[i]);
		f[i]=a[i]-a[i-1];
	}
}
void work()
{
	for(int i=2;i<=N;i++)
	{
		if(f[i]>0) p+=f[i];
		if(f[i]<0) q+=abs(f[i]);
	}
	ans1=max1(p,q);
	ans2=abs1(p-q)+1;
	cout<<ans1<<endl<<ans2<<endl;
}
int main()
{
	init();
	work();
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值