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;
}