洛谷P4552 [Poetize6] IncDec Sequence

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

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

输出描述
第一行输出最少操作次数

第二行输出最终能得到多少种结果

样例
样例运行正确并不代表程序没有漏洞,判题服务将使用大量数据对你的程序进行评测。

输入 复制
4
1
1
2
2
输出 复制
1
2
提示
对于100%的数据,n=100000,0≤ai<2147483648。
思路:

首先这道题分为两个小问(1)操作次数(2)结果种类。

(1)要做到所有数都一样,因为访问次数太多防止时间超限,可以想到分差,也就是可以让从d[2]到d[n]都为0。

							为什么d[1]可以不为0呢?

因为a[i]=a[i]+d[i-1],a[1]=a[0]+d[1],a[2]=a[3]=…a[n]=a[1]=d[1].所以呀,d[1]多大,这个数列的值就多大,题目要求所有数一样与d[1]的大小没有关系。

								怎么求最优解呢

因为这个数列是不确定的,也就是d[i]可能有正有负,而差分的性质就是让d[l]+=c,d[r+l]-=c。我们每次对d[i]进行加或减一的操作,有以下操作方式。
①d[l]++,d[r]-- 2<=l,r<=n 注意:d[l]是一个负值,d[r]是一个正值
②d[1]++,d[l]-- 2<=l<=n 注意d[l]是一个正值,也就是让数列中1~(l-1)的加一个
③d[1]–,d[l]++ 2<=l<=n 注意d[l]是一个负值 ,也就是让数列中1~(l-1)的减一个
④d[l]++,让l以后的都加一个。
⑤d[l]–,让l以后的都减一个。
那么第一种方法正数-1,负数+1,这样相当于一步里作用了两步,比让正数一个个-1和让负数一个个
+1快多了

						那么我们可以进行多少种这样的操作呢?

我们可以令差分序列里正数绝对值的总和为p,负数绝对值总和为q

可以进行这样一步顶两步的操作就是min(p,q),因为这种操作正数负数是一一配

对的,当少的那个先用完了,剩下的没有可以配对的了,只能一步步减或一步步加。

所以我们总共要进行的操作就为min(p,q)+abs(p-q),也就是max(p,q)

(2)保证最少次数的前提下,最终得到的数列有多少种?

得到的数列有多少种,其实就是问的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 <bits/stdc++.h>
using namespace std;
#define N 100010
int a[N],d[N];
int main()
{
	int n;
	cin>>n;
	int i;
	long long z=0,f=0; 
	for (i=1;i<=n;i++){
		cin>>a[i];
		if (i==1){
			continue;//因为第一个数的分差不用计较,所以输入后直接跳过 
		}
		d[i]=a[i]-a[i-1];//求出分差
		if (d[i]>0){
			z=z+d[i]; 
		}else if (d[i]<0){
			f=f+d[i];
		} 
	}
	f=abs(f);
	long long p,q;
	p=max(z,f);//p相当于是abs(z-f)+min(z,f)这个才是实际的来源 
	q=abs(z-f)+1;
	cout<<p<<endl<<q;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值