题目大意:
题目链接:https://www.luogu.org/problemnew/show/P4231
一个序列,初始值全部为0。每次修改一段区间
[
l
,
r
]
[l,r]
[l,r],区间
[
l
,
r
]
[l,r]
[l,r]中第
i
i
i个元素加上等差数列的第
i
i
i项。求最终数列的异或和以及最大值。
思路:
n
≤
1
0
7
n\leq 10^7
n≤107。连带
l
o
g
log
log的数据结构都不给过。
由于每次加的是一个等比数列,所以考虑使用差分来解决这个问题。
假设原数列为
a
a
a,差分数列为
b
b
b。
编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
a a a | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
b b b | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
这次在 [ 3 , 6 ] [3,6] [3,6]中加入等差数列,首项加 x x x,莫项加 y y y,公差为 d d d
编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
a a a | 0 | 0 | x x x | x + d x+d x+d | x + 2 d x+2d x+2d | x + 3 d ( y ) x+3d(y) x+3d(y) | 0 | 0 | 0 |
b b b | 0 | 0 | x x x | d d d | d d d | d d d | − x − 3 d ( − y ) -x-3d(-y) −x−3d(−y) | 0 | 0 |
那么现在就是一个单点修改+区间修改的问题了。用线段树时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
这样的复杂度还是不够优秀,如果可以把区间修改去掉,那么就可以直接在数列上单点修改了。
那么就在
b
b
b数组上再进行一次差分,这样就可以把
d
d
d消掉。
编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
a a a | 0 | 0 | x x x | x + d x+d x+d | x + 2 d x+2d x+2d | y y y | 0 | 0 | 0 |
b b b | 0 | 0 | x x x | d d d | d d d | d d d | − y -y −y | 0 | 0 |
c c c | 0 | 0 | x x x | d − x d-x d−x | 0 | 0 | − y − d -y-d −y−d | y y y | 0 |
所以我们每次修改时单点修改4个点就可以了。
那么考虑如何用
c
c
c数组推出最终的
a
a
a数组。
根据差分,有
a
i
=
∑
j
=
1
i
∑
x
k
=
1
j
c
x
k
a_i=\sum^{i}_{j=1}\sum^{j}_{xk=1}c_{xk}
ai=∑j=1i∑xk=1jcxk。这个
∑
x
k
=
1
j
c
x
k
\sum^{j}_{xk=1}c_{xk}
∑xk=1jcxk可以把
c
x
k
c_{xk}
cxk做一个前缀和,然后易得
a
i
=
a
i
−
1
+
s
u
m
i
a_i=a_{i-1}+sum_i
ai=ai−1+sumi。这样就
O
(
n
+
m
)
O(n+m)
O(n+m)完成了这道题。
代码:
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e7+10;
int n,m,l,r;
ll a[N],sum[N],add,x,y,ans1,ans2;
ll read()
{
ll d=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
int main()
{
n=(int)read(); m=(int)read();
for (int i=1;i<=m;i++)
{
l=(int)read(); r=(int)read(); x=read(); y=read();
add=(y-x)/(ll)(r-l);
a[l]+=x;
a[l+1]+=add-x;
a[r+1]-=add+y;
a[r+2]+=y;
}
for (int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
a[i]=a[i-1]+sum[i];
ans1^=a[i];
ans2=max(ans2,a[i]);
}
printf("%lld %lld\n",ans1,ans2);
return 0;
}