Rikka with Prefix Sum
题目描述
For example, given an array A of length n and m queries. Each query gives an interval [l,r] and you need to calculate . How to solve this problem in O(n+m)? We can calculate the prefix sum array B in which B i is equal to . And for each query, the answer is B r-B l-1.
Since Rikka is interested in this powerful trick, she sets a simple task about Prefix Sum for you:
Given two integers n,m, Rikka constructs an array A of length n which is initialized by A i = 0. And then she makes m operations on it.
There are three types of operations:
1. 1 L R w, for each index i ∈ [L,R], change A i to A i + w.
2. 2, change A to its prefix sum array. i.e., let A' be a back-up of A, for each i ∈ [1,n], change A i to .
3. 3 L R, query for the interval sum .
The first line contains a single number t(1≤ t ≤ 3), the number of the testcases.
For each testcase, the first line contains two integers n,m(1 ≤ n,m ≤ 10^5)
And then m lines follow, each line describes an operation(1 ≤ L ≤ R≤ n, 0 ≤ w ≤ 10^9).
The input guarantees that for each testcase, there are at most 500 operations of type 3.
output:
For each query, output a single line with a single integer, the answer modulo 998244353.
test:
Intput:
1
100000 7
1 1 3 1
2
3 2333 6666
2
3 2333 6666
2
3 2333 6666
output
13002
58489497
12043005
中文题意:给定一个序列A,长度最长为100000,初始值为0,现在有三种操作:
1.对区间[l,r]中所有的数都加上一个值。
2.对整个序列求一次前缀和。
3.询问[l,r]区间内所有a的和。
现在对1,0,0,0求3次前缀和得到下图
可以发现(1,1)对右下角的点的贡献是
接下来我们定义一个变量now记录数组了进行了几次求数组前缀和也就是题目的2号操作。
对于操作1,在[l,r]区间内每个数增加w。
相当于在上次进行2号操作前,在点 L 增加w,在点 R+1 减少w。
例如:在3到5号位置增加1
序号 1 2 3 4 5 6 7 8 9
now 0 0 1 1 1 0 0 0 0 求前缀和后
now-1 0 0 1 0 0 -1 0 0 0 求前缀和前
对于询问的话只要求下次求完前缀和 (位置 R 的值) - (位置 L-1 的值)
对于进行前缀和操作只要将now++即可
具体操作看代码
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn = 410000,mod=998244353; 7 struct Stack 8 { 9 ll lie,time,value; 10 } st[maxn]; 11 ll fac[maxn+10],ifac[maxn+10],top; 12 13 ll quick_pow(ll a,ll b) 14 { 15 ll ans=1; 16 while(b) 17 { 18 if(b&1) 19 ans=1ll*ans*a%mod; 20 a=1ll*a*a%mod; 21 b>>=1; 22 } 23 return ans; 24 } 25 26 void init() 27 { 28 fac[0]=1; 29 for(int i=1; i<=maxn; i++) 30 fac[i]=1ll*fac[i-1]*i%mod; 31 ifac[maxn]=quick_pow(fac[maxn],mod-2); 32 for(int i=maxn-1; i>=0; i--) 33 ifac[i]=1ll*ifac[i+1]*(i+1)%mod; 34 } 35 36 ll C(ll n,ll m) 37 { 38 return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod; 39 } 40 41 inline ll solve(ll x,ll now) 42 { 43 if(x==0)return 0; 44 45 ll sum = 0; 46 for(int i=0; i<top; i++) ///计算每个更新对点的贡献值 47 { 48 49 if(st[i].lie>x)continue; 50 ll lie = st[i].lie; 51 ll per = st[i].time; 52 ll value = st[i].value; 53 54 ll aa = x-lie + now-per -1; 55 ll bb = now-per -1; 56 57 sum = (sum + value*C(aa,bb)%mod)%mod; 58 } 59 return sum; 60 } 61 62 int main() 63 { 64 init(); ///预处理阶乘和逆元将计算组合数的时间复杂度降为O(1) 65 ll t,n,m,op; 66 scanf("%lld",&t); 67 while(t--) 68 { 69 scanf("%lld%lld",&n,&m); 70 ll now = 1,l,r,value; 71 top = 0; 72 while(m--) 73 { 74 scanf("%lld",&op); 75 if(op==1) 76 { 77 scanf("%lld%lld%lld",&l,&r,&value); 78 st[top].value = value%mod,st[top].time=now-1,st[top].lie=l; 79 top++; 80 st[top].value =(mod-value)%mod,st[top].time=now-1,st[top].lie=r+1; 81 top++; 82 ///将更新存入数组 83 } 84 else if(op==2) 85 { 86 now++; 87 } 88 else 89 { 90 scanf("%lld%lld",&l,&r); 91 ll ans = solve(r,now+1)-solve(l-1,now+1); 92 ans = (ans+mod)%mod; 93 printf("%lld\n",ans); 94 } 95 } 96 } 97 return 0; 98 }