Rikka with Prefix Sum
题解: 这道题之所以不是线段树,是因为有操作
2
2
2的存在,将数组
A
A
A转变为了它的前缀和,而且每次操作都和前缀和有关。假设我们现在有一个数组
a
=
[
0
,
0
,
0
,
0
,
0
]
a=[0,0,0,0,0]
a=[0,0,0,0,0],我们先考虑单点修改,起始我们给
a
[
0
]
+
1
a[0]+1
a[0]+1,我们做几次前缀和看一下。这里我们用
t
t
t代表第几次做前缀和。
t
=
1
,
t
=
2
,
t
=
3
,
t
=
4
,
1
1
1
1
1
1
2
3
4
5
1
3
6
10
15
1
4
10
20
35
\begin{matrix}t = 1,& \\ t = 2,\\t = 3,\\t=4,\end{matrix} \begin{matrix}1&1&1&1&1 \\1&2&3&4&5\\ 1&3&6&10&15\\ 1&4&10&20&35\end{matrix}
t=1,t=2,t=3,t=4,1111123413610141020151535
由此发现这实际上就是一个杨辉三角。因此查询的时候就会发现,只要考虑操作
1
1
1对当前位置的贡献即可。例如
(
1
,
1
)
(1,1)
(1,1)对右下角的点的贡献为
C
(
l
+
t
,
t
−
1
)
⋅
1
C(l+t,t-1)\cdot 1
C(l+t,t−1)⋅1。
但是如果是区间修改呢?
我们将操作
2
2
2考虑为时间戳。
在执行操作 1 1 1的时候在 t t t时刻对于 [ l , r ] [l,r] [l,r]加 w w w可以考虑为在 t − 1 t-1 t−1时刻对 l l l位置加 w w w,对 r − 1 r-1 r−1减去 w w w。
在执行操作 3 3 3查询的时候,查询 t t t时刻的 [ l , r ] [l,r] [l,r]的区间和相当于查询 t + 1 t+1 t+1时刻的 r r r位置的值减去 l − 1 l-1 l−1位置的值,也就是在两点修改后做一次前缀和再两点位置相减,这也就是差分思想吧。由一开始我们举例的杨辉三角,我们查询单点值,就需要考虑之前的单点修改对于当前的贡献,因此遍历之前的单点修改操作累加贡献。
而对于执行操作 2 2 2的时候,既然当做了时间戳,那么就 t + 1 t+1 t+1即可。
代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 200100, mod = 998244353;
struct node{
int p, t, w;
node() {}
node(int x,int y,int z) {p = x; t = y; w = z;}
}num[N];
LL fac[N<<1], Inv[N<<1], d = 0;
LL Qpow(LL a,LL n)
{
LL res = a;
LL ans = 1;
while(n){
if(n & 1)
ans = ans * res % mod;
res = res * res % mod;
n >>= 1;
}
return ans % mod;
}
LL C(LL n, LL m)
{
return fac[n] * Inv[m] % mod * Inv[n - m] % mod;
}
LL query(int x,int t)
{
if(x == 0) return 0;
LL sum = 0;
for(int i = 0; i < d; ++i){
if(num[i].p <= x){
sum = (sum + 1LL * C(x - num[i].p + t - num[i].t - 1, t - num[i].t - 1) * num[i].w %mod)%mod;
}
}
return sum;
}
void init()
{
fac[0] = Inv[0] = 1;
for(int i = 1; i <= N; ++i) fac[i] = 1LL * i * fac[i - 1] % mod;
Inv[N] = Qpow(fac[N], mod - 2);
// printf("%lld %lld\n",Inv[N],fac[N]);
for(int i = N - 1; i >= 1; --i) Inv[i] = 1LL * (i + 1) * Inv[i + 1] % mod;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
#endif
init();
int T,n,m;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int t = 1, op, l, r, w;
d = 0;
for(int i = 0; i < m; ++i){
scanf("%d",&op);
if(op == 1){
scanf("%d%d%d",&l,&r,&w);
num[d++] = node(l, t - 1, w);
num[d++] = node(r + 1, t - 1, mod - w);
}else if(op == 2){
t++;
}else{
scanf("%d%d",&l,&r);
printf("%lld\n",((query(r, t + 1) - query(l - 1, t + 1))%mod + mod)%mod);
}
}
}
return 0;
}