题目链接:传送门
题外话:这是一个蒟蒻在看不懂所有题解时,受到巨神mhy的帮助后写的题解。
我太蒻了qwq
题目描述
By the age of three Smart Beaver mastered all arithmetic operations and I love zwt got this summer homework from the amazed teacher:
You are given a sequence of integers a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1, a2, ..., an. Your task is to perform on it m consecutive operations of the following type:
For given numbers
x
i
x_i
xi and
v
i
v_i
vi assign value
v
i
v_i
vi to element
a
x
i
a_{x_i}
axi.
For given numbers
l
i
l_i
li and
r
i
r_i
ri you’ve got to calculate sum
∑
x
=
0
r
i
−
l
i
f
x
⋅
a
l
i
+
x
\sum_{x=0}^{r_i-l_i}{f_x·a_{l_i+x}}
∑x=0ri−lifx⋅ali+x, where
f
0
=
f
1
=
1
f_0 = f_1 = 1
f0= f1 = 1 and at
i
≥
2
:
f
i
=
f
i
−
1
+
f
i
−
2
i ≥ 2: f_i = f_{i-1} + f_{i-2}
i ≥ 2:fi = fi−1 + fi−2.
For a group of three numbers
l
i
,
r
i
,
d
i
l_i,r_i,d_i
li,ri,di you should increase value
a
x
a_x
ax by
d
i
d_i
di for all
x
(
l
i
≤
x
≤
r
i
)
x (l_i ≤ x ≤ r_i)
x(li ≤ x ≤ ri).
Smart Beaver planned a tour around great Canadian lakes, so he asked you to help him solve the given problem.
Input
The first line contains two integers n and m
(
1
≤
n
,
m
≤
2
⋅
1
0
5
)
(1 ≤ n, m ≤ 2·10^5)
(1 ≤ n, m ≤ 2⋅105) — the number of integers in the sequence and the number of operations, correspondingly. The second line contains
n
n
n integers
a
1
,
a
2
,
.
.
.
,
a
n
(
0
≤
a
i
≤
1
0
5
)
a_1, a_2, ..., a_n (0 ≤ a_i ≤ 10^5)
a1, a2, ..., an(0 ≤ ai ≤ 105). Then follow
m
m
m lines, each describes an operation. Each line starts with an integer
t
i
t_i
ti
(
1
≤
t
i
≤
3
)
(1 ≤ t_i ≤ 3)
(1 ≤ ti ≤ 3) — the operation type:
if
t
i
=
1
t_i = 1
ti = 1, then next follow two integers
x
i
,
v
i
(
1
≤
x
i
≤
n
,
0
≤
v
i
≤
1
0
5
)
x_i,v_i (1 ≤ x_i ≤ n, 0 ≤ v_i ≤ 10^5)
xi,vi(1 ≤ xi ≤ n, 0 ≤ vi ≤ 105);
if
t
i
=
2
t_i = 2
ti = 2, then next follow two integers
l
i
,
r
i
(
1
≤
l
i
≤
r
i
≤
n
)
l_i,r_i (1 ≤ l_i ≤ r_i ≤ n)
li,ri(1 ≤ li ≤ ri ≤ n);
if
t
i
=
3
t_i = 3
ti = 3, then next follow three integers
l
i
,
r
i
,
d
i
(
1
≤
l
i
≤
r
i
≤
n
,
0
≤
d
i
≤
1
0
5
)
l_i,r_i,d_i (1 ≤ l_i ≤ r_i ≤ n, 0 ≤ d_i ≤ 10^5)
li,ri,di(1 ≤ li ≤ ri ≤ n, 0 ≤ di ≤ 105).
The input limits for scoring 30 points are (subproblem E1):
It is guaranteed that n does not exceed 100, m does not exceed 10000 and there will be no queries of the 3-rd type.
The input limits for scoring 70 points are (subproblems E1+E2):
It is guaranteed that there will be queries of the 1-st and 2-nd type only.
The input limits for scoring 100 points are (subproblems E1+E2+E3):
No extra limitations.
Output
For each query print the calculated sum modulo 1000000000 (
1
0
9
10^9
109).
题意解释
给出n个数,m个询问,
要求:
1.支持区间修改和单点修改。
2.支持询问
∑
x
=
0
r
i
−
l
i
f
x
⋅
a
l
i
+
x
\sum_{x=0}^{r_i-l_i}{f_x·a_{l_i+x}}
∑x=0ri−lifx⋅ali+x,其中
f
x
f_x
fx表示第x个斐波那契数。
大致思路
注:题面中的
f
f
f数组下标是从0开始的,此篇题解的下标从1开始,因此有些地方与题面不一样。
首先观察数据范围:
n
<
=
2
⋅
1
0
5
n<=2·10^5
n<=2⋅105,又要支持单点修改和区间修改,想到用线段树。
此题询问的信息比较特殊,考虑引进S:
在每个线段树节点中,令
[
l
,
r
]
[l,r]
[l,r]表示此节点的区间。
令:
S
1
=
f
1
a
l
+
f
2
a
l
+
1
+
f
3
a
l
+
2
+
.
.
.
+
f
r
−
l
+
1
a
r
S_1=f_1a_l+f_2a_{l+1}+f_3a_{l+2}+...+f_{r-l+1}a_r
S1=f1al+f2al+1+f3al+2+...+fr−l+1ar
S
2
=
f
2
a
l
+
f
3
a
l
+
1
+
f
4
a
l
+
2
+
.
.
.
+
f
r
−
l
+
2
a
r
S_2=f_2a_l+f_3a_{l+1}+f_4a_{l+2}+...+f_{r-l+2}a_r
S2=f2al+f3al+1+f4al+2+...+fr−l+2ar
S
3
=
f
3
a
l
+
f
4
a
l
+
1
+
f
5
a
l
+
2
+
.
.
.
+
f
r
−
l
+
3
a
r
S_3=f_3a_l+f_4a_{l+1}+f_5a_{l+2}+...+f_{r-l+3}a_r
S3=f3al+f4al+1+f5al+2+...+fr−l+3ar
.
.
.
.
.
.
......
......
S
x
=
f
x
a
l
+
f
x
+
1
a
l
+
1
+
f
x
+
2
a
l
+
2
+
.
.
.
+
f
r
−
l
+
x
a
r
S_x=f_xa_l+f_{x+1}a_{l+1}+f_{x+2}a_{l+2}+...+f_{r-l+x}a_r
Sx=fxal+fx+1al+1+fx+2al+2+...+fr−l+xar
找规律,发现:
S
1
+
S
2
S_1+S_2
S1+S2
=
(
f
1
+
f
2
)
a
l
+
(
f
2
+
f
3
)
a
l
+
1
+
(
f
3
+
f
4
)
a
l
+
2
+
.
.
.
+
(
f
r
−
l
+
1
+
f
r
−
l
+
2
)
a
r
=(f_1+f_2)a_l+(f_2+f_3)a_{l+1}+(f_3+f_4)a_{l+2}+...+(f_{r-l+1}+f_{r-l+2})a_r
=(f1+f2)al+(f2+f3)al+1+(f3+f4)al+2+...+(fr−l+1+fr−l+2)ar
=
f
3
a
l
+
f
4
a
l
+
1
+
f
5
a
l
+
2
+
.
.
.
+
f
r
−
l
+
3
a
r
=f_3a_l+f_4a_{l+1}+f_5a_{l+2}+...+f_{r-l+3}a_r
=f3al+f4al+1+f5al+2+...+fr−l+3ar
=
S
3
=S_3
=S3
如果再推下去,则会发现 S 2 + S 3 = S 4 S_2+S_3=S_4 S2+S3=S4, S 3 + S 4 = S 5 . . . . . . . . S_3+S_4=S_5........ S3+S4=S5........
因此,可以证明 S x = S x − 1 + S x − 2 S_x=S_{x-1}+S_{x-2} Sx=Sx−1+Sx−2。
再把式中的 S x − 1 , S x − 2 S_{x-1},S_{x-2} Sx−1,Sx−2分解下去,得 S x = . . . = f x − 2 S 1 + f x − 1 S 2 S_x=...=f_{x-2}S_1+f_{x-1}S_2 Sx=...=fx−2S1+fx−1S2。
因此,只要确定了某个节点的 S 1 , S 2 S_1,S_2 S1,S2,就能通过预处理好的 f f f数组来快速得到 S x S_x Sx的值。
所以,线段树中维护
S
1
,
S
2
S_1,S_2
S1,S2两个值,用
l
a
z
y
t
a
g
lazytag
lazytag来维护区间加标记。
接下来是具体操作:
如何得到答案(即Query函数)
假设需要查询的区间
[
l
,
r
]
[l,r]
[l,r]是黑色部分,当前线段树节点的区间
[
L
,
R
]
[L,R]
[L,R]是红色部分:
需要求的值是黑色字体部分,线段树中维护的值是红色字体部分:
那么,如何用线段树维护的
S
1
,
S
2
S_1,S_2
S1,S2信息得到上面一大坨黑色字体的结果呢?
由于线段树Query是递归进行的,因此在当前线段树区间是
[
L
,
R
]
[L,R]
[L,R]时,需要求得的值是:
f
L
−
l
+
1
a
L
+
.
.
.
+
f
R
−
l
+
1
a
R
f_{L-l+1}a_L+...+f_{R-l+1}a_R
fL−l+1aL+...+fR−l+1aR。
观察发现,这个值就是当前区间的
S
L
−
l
+
1
S_{L-l+1}
SL−l+1!
又因为上面已经证明过
S
x
=
f
x
−
2
S
1
+
f
x
−
1
S
2
S_x=f_{x-2}S_1+f_{x-1}S_2
Sx=fx−2S1+fx−1S2,
所以预处理出
f
f
f数组,然后大力
O
(
1
)
O(1)
O(1)算出此时的
S
L
−
l
+
1
S_{L-l+1}
SL−l+1即可。
区间修改(即Update函数)
单点修改很简单,这里就不讲了。。
回到图片,假设当前线段树区间为
[
L
,
R
]
[L,R]
[L,R],区间加
v
v
v,用
S
1
S_1
S1举例:
(图中打错了,应该是
S
1
S_1
S1而不是
S
0
S_0
S0)
计算差值,发现
S
0
S_0
S0增加的值为
F
1
⋅
v
+
F
2
⋅
v
+
.
.
.
+
F
R
−
L
+
1
⋅
v
F_1·v+F_2·v+...+F_{R-L+1}·v
F1⋅v+F2⋅v+...+FR−L+1⋅v,
因此可以处理出
f
f
f数组的前缀和,然后大力算出
S
0
S_0
S0增加的值即可。
S
1
S_1
S1的维护同理。
维护信息(即Pushup函数)
以
S
1
S_1
S1为例。
P
u
s
h
u
p
Pushup
Pushup如下 不画图了,偷一波懒:
tree[rt].s1=(tree[lc].s1+S(rc,mid-tree[rt].l+2))%mod;
其中tree[rt]表示当前的线段树节点,lc表示左孩子的下标,rc表示右孩子的下标。
如何证明这样维护是正确的呢?
这里分别展开等式的左右两端:
设当前区间为
[
l
,
r
]
[l,r]
[l,r]。
左边=
F
1
a
l
+
F
2
a
l
+
1
+
.
.
.
+
F
r
−
l
+
1
a
r
F_1a_l+F_2a_{l+1}+...+F_{r-l+1}a_r
F1al+F2al+1+...+Fr−l+1ar
令mid为当前区间的中点,即
m
i
d
=
(
l
+
r
)
/
2
mid=(l+r)/2
mid=(l+r)/2。
右边=
(
F
1
a
l
+
F
2
a
l
+
1
+
.
.
.
+
F
m
i
d
−
l
+
1
a
m
i
d
)
+
S
m
i
d
−
l
+
2
(F_1a_l+F_2a_{l+1}+...+F_{mid-l+1}a_{mid})+S_{mid-l+2}
(F1al+F2al+1+...+Fmid−l+1amid)+Smid−l+2
=
(
F
1
a
l
+
F
2
a
l
+
1
+
.
.
.
+
F
m
i
d
−
l
+
1
a
m
i
d
)
+
(
F
m
i
d
−
l
+
2
a
m
i
d
+
1
+
.
.
.
+
F
r
−
l
+
1
a
r
)
=(F_1a_l+F_2a_{l+1}+...+F_{mid-l+1}a_{mid})+(F_{mid-l+2}a_{mid+1}+...+F_{r-l+1}a_r)
=(F1al+F2al+1+...+Fmid−l+1amid)+(Fmid−l+2amid+1+...+Fr−l+1ar)
=
F
1
a
l
+
F
2
a
l
+
1
+
.
.
.
+
F
r
−
l
+
1
a
r
=F_1a_l+F_2a_{l+1}+...+F_{r-l+1}a_r
=F1al+F2al+1+...+Fr−l+1ar
=
=
=左边
因此,这样维护是正确的qwq。
下推标记(即Pushdown函数)
类比Update即可。
代码
请原谅我的毒瘤码风qwq
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=200005;
const ll mod=1e9;
int n,m,a[Size];
ll f[Size],sumf[Size];
ll f2[Size],sumf2[Size];
struct node {
int l,r;
ll s1,s2;
ll lazy;
inline int len() {
return r-l+1;
}
} tree[Size<<2];
inline ll S(int rt,int x) {
if(x==1) return tree[rt].s1;
if(x==2) return tree[rt].s2;
return (f[x-2]*tree[rt].s1%mod+f[x-1]*tree[rt].s2%mod)%mod;
}
inline void Pushup(int rt) {
int mid=(tree[rt].l+tree[rt].r)>>1;
tree[rt].s1=(tree[lc].s1+S(rc,mid-tree[rt].l+2))%mod;
tree[rt].s2=(tree[lc].s2+S(rc,mid-tree[rt].l+3))%mod;
}
void Build(int l,int r,int rt) {
tree[rt].l=l;
tree[rt].r=r;
if(l==r) {
tree[rt].s1=tree[rt].s2=a[l];
return;
}
int mid=(l+r)>>1;
Build(l,mid,lc);
Build(mid+1,r,rc);
Pushup(rt);
}
void Pushdown(int rt) {
if(tree[rt].lazy) {
int lenl=tree[lc].len(),lenr=tree[rc].len();
tree[lc].s1=(tree[lc].s1+sumf[lenl]*tree[rt].lazy)%mod;
tree[lc].s2=(tree[lc].s2+sumf2[lenl]*tree[rt].lazy)%mod;
tree[rc].s1=(tree[rc].s1+sumf[lenr]*tree[rt].lazy)%mod;
tree[rc].s2=(tree[rc].s2+sumf2[lenr]*tree[rt].lazy)%mod;
tree[lc].lazy+=tree[rt].lazy;
tree[rc].lazy+=tree[rt].lazy;
tree[rt].lazy=0;
}
}
void UpdateNode(int x,int v,int rt) {
if(tree[rt].l==tree[rt].r) {
tree[rt].s1=tree[rt].s2=v;
return;
}
Pushdown(rt);
int mid=(tree[rt].l+tree[rt].r)>>1;
if(x<=mid) UpdateNode(x,v,lc);
if(x>mid) UpdateNode(x,v,rc);
Pushup(rt);
}
void Update(int l,int r,ll v,int rt) {
if(l<=tree[rt].l && tree[rt].r<=r) {
int len=tree[rt].len();
tree[rt].s1=(tree[rt].s1+sumf[len]*v)%mod;
tree[rt].s2=(tree[rt].s2+sumf2[len]*v)%mod;
tree[rt].lazy+=v;
return;
}
Pushdown(rt);
int mid=(tree[rt].l+tree[rt].r)>>1;
if(l<=mid) Update(l,r,v,lc);
if(r>mid) Update(l,r,v,rc);
Pushup(rt);
}
ll Query(int l,int r,int rt) {
if(l<=tree[rt].l && tree[rt].r<=r) {
return S(rt,tree[rt].l-l+1);
}
Pushdown(rt);
int mid=(tree[rt].l+tree[rt].r)>>1;
ll ans=0;
if(l<=mid) ans=(ans+Query(l,r,lc))%mod;
if(r>mid) ans=(ans+Query(l,r,rc))%mod;
return ans;
}
void GetFib() {
f[1]=f[2]=1;
sumf[1]=1;
sumf[2]=2;
f2[1]=1; f2[2]=2;
sumf2[1]=1;
sumf2[2]=3;
for(re i=3; i<=n; i++) {
f[i]=(f[i-1]+f[i-2])%mod;
sumf[i]=(sumf[i-1]+f[i])%mod;
f2[i]=(f2[i-1]+f2[i-2])%mod;
sumf2[i]=(sumf2[i-1]+f2[i])%mod;
}
}
int main() {
n=read();
m=read();
for(re i=1; i<=n; i++) a[i]=read();
GetFib();
Build(1,n,1);
int x,v,l,r;
while(m--) {
int t=read();
if(t==1) {
x=read();
v=read();
UpdateNode(x,v,1);
} else if(t==2) {
l=read();
r=read();
printf("%lld\n",Query(l,r,1));
} else {
l=read();
r=read();
v=read();
Update(l,r,v,1);
}
}
return 0;
}