CFdiv2-Helping the Nature-(差分整理)

C

题意:
就是给你n个数,然后每次你可以把1到i这些点的数都-1,或者i到n这些点的数-1,或者整个数组都加1。问你最小多少次可以让整个数组变成0。

思考:
刚开始想了下贪心,但是发现贪心很麻烦。于是去想二分,二分肯定只能用强制性判断,但是想了想没法去强制性。然后感觉基础的做法不行之后,感觉好像是差分,但是掌握的不是太牢固,就不知道怎么想下去了。所以一定要把基础打牢。
其实这个就是把2到n点的差分维护出来以后,既然想要整个数组为0,那么首先让2到n点的差分都要变成0,只有这样每个数才会和前面的那个数相等,也就是此时所有的数都等于vb[1]。既然要让数组为0,那么此时让vb[1]=0即可。所以让vb[1]=0需要花多少操作,那就看看后面有多少让1到i都减1。当然还要看看这个数组本身第一位的数字最终也要减去。
还有就是对于这种给你操作前缀和后缀的题目,让整个数组都相同的代价,就是整个数组每两个相邻的数组差的绝对值,就是根据差分都让他们一样的原理来的。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;

int T,n,m,k;
int va[N];

signed main()
{
	IOS;
	cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i=1;i<=n;i++) cin>>va[i];
		int ans = 0,sum = 0;
		for(int i=2;i<=n;i++)
		{
			ans += abs(va[i]-va[i-1]);
			if(va[i-1]>va[i]) sum += va[i-1]-va[i];
		}
		cout<<ans+abs(va[1]-sum)<<"\n";
	}
	return 0;
}

增减序列

题意:
就是给你一个数组,然后每次可以选择一个区间[l,r],让这个区间的数都加1或者减1。问你至少多少次可以让整个数组中的数都一样,并在操作次数最小的前提下,最终得到的数列有多少种可能。

思考:
这就是应用差分,最经典的题目。要让整个数组都一样,那么要让2到n点的差分数组都为0,这样这个数组的数都等于vb[1]。问你最少的操作次数,首先要知道,对原数组的区间操作,会在差分数组中如何体现。对于对原数组的区间[l,r]加多少减多少的操作,在差分数组里面的体现就是,vb[l]处加减多少,vb[r+1]处加减多少,这个写几个数推一推就看出来了。
发现要差分数组里面有正数和负数,那么操作区间的时候,可以让这些正数负数尽可能的抵消。这样变成0的操作次数可以最小。对于多出来的正数或者负数,可以和最后一个数匹配作为区间,这样差分数组也只会影响这一个点,因为影响的r+1>n了都。所以最终的可能的结果是什么呢,首先可能就是vb[1],因为把剩下的都给最后面匹配了。要么就是剩下的数的个数。因为可以和1匹配,也可以和最后面匹配,所以最终就有(1+剩下的数),比如正数一共a个,负数b个。那么就有abs(a-b)+1种可能。
2017EC-Straight Master。这就是一道差分数组应用的真题。
在这里插入图片描述

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;

int T,n,m,k;
int va[N];
int vb[N];

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>va[i];
	int a = 0,b = 0;
	for(int i=2;i<=n;i++)
	{
		vb[i] = va[i]-va[i-1];
		if(vb[i]>=0) a += vb[i];
		else b += abs(vb[i]);
	}
	cout<<max(a,b)<<"\n"<<abs(a-b)+1<<"\n";
	return 0;
}

总结:
多多思考呀,一定要掌握本质,稳固基础。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值