差分+贪心:IncDec序列

原题

题目描述
给定一个长度为 n 的数列 a1,a2,…,ana1,a2,…,an,每次可以选择一个区间 [l,r][l,r],使下标在这个区间内的数都加一或者都减一。

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

输入格式
第一行输入正整数n。

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

输出格式
第一行输出最少操作次数。

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

数据范围
0<n≤1050<n≤105
0≤ai<21474836480≤ai<2147483648
输入样例:

4
1
1
2
2
输出样例:

1
2

思路:首先这题一个重点,就是区间[l,r][l,r]的修改操作。因为这道题目的修改操作有一个特性,就是只加一或者只减一,而不是+x+x,也不是−x−x,所以说我们并不需要用到高级的数据结构,线段树和树状数组,而只是需要用差分即可。
差分:差分的定义,可以见《算法竞赛进阶指南》的P21页。
介于第一版没有差分,这里说一下定义:对于一个给定的数列A,它的差分数列B定义为, B[1]=A[1],B[i]=Ai−Ai−1(2<=i<=n)B[1]=A[1],B[i]=Ai−Ai−1(2<=i<=n)
这里只说性质,也就是把序列A的区间[L,R][L,R]加d,也就是把Al,Al+1....ArAl,Al+1....Ar都加上d,其实就是它的差分序列B中,Bl+d,Br+1−dBl+d,Br+1−d,其他的位置统统不改变。
因此在这道题目中,我们就可以利用这个非常有用的性质,因为我们只要求A序列中所有的数相同,而不在意这些方案具体是什么,所以说我们就可以转化题目,也就是将对A序列的+1,−1+1,−1操作,让A序列相同,改成目标把B2,…,BnB2,…,Bn变成全0即可,也就是A序列全部相等。而且最后得到的序列,就是这n个B1B1
贪心:因为我们有上面所说的性质,那么我们就可以,每一次选取Bi和BjBi和Bj,2<=i,j<=n2<=i,j<=n,而且这两个数,一个为正数,一个为负数,至于为什么要是正负配对,因为我们是要这个B序列2~n都要为0,所以这样负数增加,正数减少,就可以最快地到达目标为0的状态。
至于那些无法配对的数BkBk可以选B1B1或者BnBn,这两个不影响的数,进行修改。
所以说我们这道题目得出的答案就是,最少操作数min(p,q)+abs(p−q)=max(p,q)min(p,q)+abs(p−q)=max(p,q),然后最终序列a可能会有abs(p−q)+1abs(p−q)+1种情况。p为b序列中正数之和,而q为b序列中负数之和

作者:秦淮岸灯火阑珊
链接:https://www.acwing.com/solution/acwing/content/816/
来源:AcWing

代码:·要开long long!!!!(别问我为什么怎么说)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
long long a[N],b[N],n;
int main(){
	std::ios::sync_with_stdio(false);
	cin>>n;
	long long zans=0,fans=0,ans1,ans2;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];
	for(int i=2;i<=n;i++)
	if(b[i]>0)zans+=b[i];
	else fans+=b[i];
	ans2=abs(fans+zans);
	fans=abs(fans);
	ans1=max(fans,zans);
	cout<<ans1<<endl<<ans2+1; 
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/myhnb/p/11248066.html