CodeForces - 862E Mahmoud and Ehab and the function 二分(思维题)

传送门:Codeforces 862E

题意:给出长为n的a序列和长为m的b序列,求

.

的最小值,其中0 <= j <= m - n

还有q次操作,每次操作将a[l..r]区间内的数 + x, 每次操作后求一次f(j)的最小值

思路:容易得到无论a序列的数怎么变化,每个位置上的数的加减性质是不会变的,即奇数位置上的数一直是加,偶数位置上的减,因此我们将f(j)的值拆成a序列的贡献和b序列的贡献两部分,无论j怎么变化,a序列的贡献是不会变化的,

而当a[l..r]区间更新时,a序列的贡献变化只由l..r区间长度和l的位置决定,并且可以O(1)求出来。

下面考虑b序列的贡献,b序列对所有f(j)的贡献一共有m - n + 1 种,而这些种贡献都是由b序列中一个长度为n的连续子序列产生的,因此我们可以用滑动窗口的方式在O(m)的时间内求出所有种b的贡献。具体细节看代码。

求出b序列的所有贡献后,每次要输出结果的时候二分一个最接近a序列贡献值的数就好了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define pi acos(-1)
#define MAXN 100010
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
ll a[MAXN], b[MAXN], all;
int n, m, q;
vector<ll> sum;
ll get(int id)
{
	if(id == m - n + 1) return fabs(all - sum[id - 1]);
	if(id == 0) return fabs(all - sum[id]);
	return min(fabs(all - sum[id]), fabs(all - sum[id - 1]));
}
int main()
{
	ll tmp = 0, x;
	cin >> n >> m >> q;
	for(int i = 1; i <= n; i++)
	scanf("%lld", &a[i]), all += (i & 1 ? a[i] : -a[i]);
	for(int i = 1; i <= m; i++)
	{
		scanf("%lld", &b[i]);
		if(i <= n) tmp += (i & 1 ? b[i] : -b[i]);
		else{
			tmp -= b[i - n];//这两步是思维点
			tmp = -tmp + (n & 1 ? b[i] : -b[i]);//注意这里是判断n的奇偶
		}
		if(i >= n)
		sum.push_back(tmp);
	}
	sort(sum.begin(), sum.end());
	tmp = lower_bound(sum.begin(), sum.end(), all) - sum.begin();
	printf("%lld\n", get(tmp));
	int l, r;
	while(q--)
	{
		scanf("%d %d %lld", &l, &r, &x);
		if((r - l + 1) & 1){//修改a序列贡献
			if(l & 1) all += x;
			else all -= x;
		}
		printf("%lld\n", get(lower_bound(sum.begin(), sum.end(), all) - sum.begin()));
	}
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值