Rikka with Prefix Sum(组合数学)

Rikka with Prefix Sum

题目描述

Prefix Sum is a useful trick in data structure problems.
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 + 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 .
Intput:

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 }
View Code

 

 

转载于:https://www.cnblogs.com/xcantaloupe/p/9511909.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值