链接:https://www.nowcoder.com/acm/contest/148/D
来源:牛客网
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 Bi is equal to . And for each query, the answer is Br-Bl-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 Ai = 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 Ai to Ai + 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 Ai 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 ≤ 105). And then m lines follow, each line describes an operation(1 ≤ L ≤ R≤ n, 0 ≤ w ≤ 109). The input guarantees that for each testcase, there are at most 500 operations of type 3.
For each query, output a single line with a single integer, the answer modulo 998244353.
示例1
复制
1 100000 7 1 1 3 1 2 3 2333 6666 2 3 2333 6666 2 3 2333 6666
复制
13002 58489497 12043005
思路:第一眼看到这题很容易往数据结构线段树上想,但每次求前缀O(n),肯定会T,这道题其实是和组合数有关的,按照样例给1到3加1然后多次球前缀和
1 1 1 0 0 0
1 2 3 3 3 3
1 3 6 9 12 15
1 4 10 19 31 46
1 5 15 34 65 111
我么可以发现前3列其实和杨晖三角有关的,我们斜着看的话,就是杨晖三角
C(0,0) C(1,0) C(2,0)
C(1,1) C(2,1) C(3,1)
C(2,2) C(3,2) C(4,2)
C(3,3) C(4,3) C(5,3)
C(4,4) C(5,4) C(6,4)
但是后三列很难找出规律来,我们可以把1操作分解为两个1操作,l到n我们全加上1,r+1到n全加 -1,我们可以发现下面左边的矩阵+右面的矩阵等于上面的矩阵,所以这样做是正确的,比较巧妙
1 1 1 1 1 1 0 0 0 -1 -1 -1
1 2 3 4 5 6 0 0 0 -1 -2 -3
1 3 6 10 15 21 0 0 0 -1 -3 -6
1 4 10 20 35 56 0 0 0 -1 -4 -10
1 5 15 35 70 126 0 0 0 -1 -5 -15
每次3操作的时候,需要把这次3操作之前的2操作和1操作都记录下来,我们记录一二操作的次数,每次一操作对应的二操作次数,以及一操作的左边界P1,和添加的值,查询[l,r]的和,转化为P1到r的和减去P1到l-1的和,要判断一下P1和r、P1和l-1的关系,因为一操作只影响右面的数,根据组合公式
C(q,q)+C(q+1,q)+ C(q+2,q)+ C(q+3,q)+ C(q+4,q)+ C(q+R+1,q)= C(q+1,q+R)求和
#include
using namespace std;
const int MAXN = 3 * 1e5 + 10;
const int MOD = 998244353;
typedef long long ll;
//--------------------组合数-----------------------
ll fac[MAXN],inv[MAXN];
ll q_pow(ll a,int b)
{
ll res = 1,base = a;
while(b) {
if(b & 1) res = res * base % MOD;
base = base * base % MOD;
b >>= 1;
}
return res;
}
ll C(int n,int k)
{
return fac[n] * inv[k] % MOD * inv[n - k] % MOD;
}
void init()
{
//inv2 = q_pow(2,MOD - 2);
fac[0] = fac[1] = 1;
for(int i = 2; i < MAXN; i++) {
fac[i] = i * fac[i - 1] % MOD;
}
inv[MAXN - 1] = q_pow(fac[MAXN - 1],MOD - 2);
for(int i = MAXN - 2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) % MOD;
}
}
//--------------------组合数-----------------------
struct node
{
int pos,num,val;
}op1[MAXN];
int cnt1,cnt2;
ll query(int pos)
{
ll res = 0;
int q;
for(int i = 0; i < cnt1; i++) {
q = cnt2 - op1[i].num;
if(pos >= op1[i].pos) {
//printf("%d----%d\n",q + pos - op1[i].pos + 1,q + 1);
res = (res + C(q + pos - op1[i].pos + 1,q + 1) % MOD * (ll)op1[i].val % MOD) % MOD;
}
}
return res;
}
int main(void)
{
int T,n,m,op,val,l,r;
scanf("%d",&T);
init();
while(T--) {
scanf("%d %d",&n,&m);
cnt1 = 0,cnt2 = 0;
while(m--) {
scanf("%d",&op);
if(op == 1) {
scanf("%d %d %d",&l,&r,&val);
op1[cnt1].num = cnt2,op1[cnt1].pos = l,op1[cnt1++].val = val % MOD;
op1[cnt1].num = cnt2,op1[cnt1].pos = r + 1,op1[cnt1++].val = - val % MOD;
}
else if(op == 2) {
cnt2++;
}
else {
scanf("%d %d",&l,&r);
// printf("%lld\n",( (query(r) - query(l - 1) + MOD) % MOD) );
printf("%lld\n",( ((query(r) - query(l - 1)) % MOD + MOD) % MOD) );
}
}
}
return 0;
}
/*
5
7 8
1 1 1 1
2
2
2
2
3 1 5
2
100000 10000
1 1 3 1
2
3 2333 6666
2
3 2333 6666
2
3 2333 6666
1 3 5 2
2
3 2 7
1 4 7 3
2
2
3 435 4534
1 8 88 32467
2
2
3 56 917
*/