P4231 三步必杀

题目描述

洛谷

长度为 \(n\) 的序列,\(m\) 次操作,每次操作区间加等差数列,最后询问序列元素的异或和。

数据范围: \(n\leq 10^7, m\leq 3\times 10^5\)

solution

我们手玩一下样例,对等差数列进行差分,会得到一个这样的数列

2,2,2,2,2,-10

发现我们还要每次都遍历一遍,给差分数组加上,这样复杂度会很高。

我们考虑对差分数组在进行一遍差分,就会变成

2 0 0 0 0 ,-12,12

这样你就会发现,我们只要给四个位置加上就行了。

分别是 dd[l] ,dd[l+1],dd[r+1],dd[r+2](dd为对差分数组进行差分之后的结果)

设等差数列的公差为t,首项为st,尾项为en

那么 dd[l] += st; dd[l+1] += t-st; dd[r+1] -= en+t; dd[e+2] += en+t;

之后我们对dd数组求一下前缀和,就可以得到差分数组。

在对差分数组求一下前缀和,就是改变后的序列.

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,l,r,st,en,t;
long long ans,maxn,d[10000010],sum[10000010],a[10000010];
inline int read()
{
	int s = 0, w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
	return s * w;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= m; i++)
	{
		l = read(); r = read(); st = read(); en = read();
		t = (en - st) / (r-l);//t是等差数列的公差
		d[l] += st;//上面的柿子
		d[l+1] += t-st;
		d[r+1] -= en+t;
		d[r+2] += en;//d是对差分数组差分得到的结果
	}
	for(int i = 1; i <= n; i++)
	{
		a[i] = a[i-1] + d[i];//a为差分数组
		sum[i] = sum[i-1] + a[i];//sum为改变之后的序列
		ans ^= sum[i];
		maxn = max(maxn,sum[i]); 
	}
	printf("%lld %lld\n",ans,maxn);
	return 0;
}

ENDING

posted @ 2020-08-02 07:27  genshy  阅读(179)  评论(0编辑  收藏  举报