牛客多校第十场 Rikka with Prefix Sum(组合数)

链接: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
*/

 

你可能感兴趣的:(ACM-数学)