牛客第十场: 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 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
100000 7
1 1 3 1
2
3 2333 6666
2
3 2333 6666
2
3 2333 6666

 

样例输出

13002
58489497
12043005

给定一个长度为 nn 初始全为 00 的数列 AA 。mm 次操作,要求支持以下三种操作。

  1. 区间加一个数 vv

  2. 全局修改,对于每一个 ii ,把 AiAi 改成原序列前 ii 项的和。

  3. 区间求和。(询问次数不超过500)

  n,m≤105,v≤109

我们考虑在x处一点,取k次)前缀和后,对y的贡献。通过尝试写出几次操作,列出每次操作后的字序列结果,可以得出,如果在(i,j)点+w,那么(x,y)点的系数为C(x-ai+y-j-1,x-i-1)。这里,其实在处理过程中,用到差分序列,可以参考:https://blog.csdn.net/wu_tongtong/article/details/79115921

#include

using namespace std;
typedef long long ll;
const int mod=998244353;
const int N=2e5+10;
ll fac[N],inv[N];
int cnt;
struct node
{
    int x,pos,w;
}a[N];
ll qmod(ll x,ll y)
{
    x%=mod;
    ll ans=1;
    while(y)
    {
        if(y&1)
        {
            ans=ans*x%mod;
        }
        x=x*x%mod;
        y>>=1;
    }
    return ans;
}
void pre()
{
    fac[0]=1;
    for(int i=1;i<=N;i++)
    {
        fac[i]=fac[i-1]*i%mod;
    }
    inv[N-1]=qmod(fac[N-1],mod-2);
    for(int i=N-2;i>=0;i--)
    {
        inv[i]=inv[i+1]*(i+1)%mod;
    }
}
ll C(int a,int b)
{
    if(b>a||b<0)
    {
        return 0;
    }
    return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
ll fun(int x,int y)
{
    ll ans=0;
    for(int i=1;i<=cnt;i++)
    {
        if(a[i].x<=x&&a[i].pos<=y)
        {
            ans=(ans+C(x-a[i].x+y-a[i].pos-1,x-a[i].x-1)*(ll)a[i].w%mod)%mod;
        }
    }
    return ans;
}
int main()
{
    pre();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);
        int now=1;
        cnt=0;
        int op,l,r,w;
        while(m--)
        {
            scanf("%d",&op);
            if(op==1)//操作1,把单点修改的操作记下来
            {
                scanf("%d %d %d",&l,&r,&w);
                cnt++;
                a[cnt].x=now-1;
                a[cnt].pos=l;
                a[cnt].w=w%mod;
                cnt++;
                a[cnt].x=now-1;
                a[cnt].pos=r+1;
                a[cnt].w=-w%mod;
            }
            else if(op==2)//操作2,只需要取前缀和的次数++
            {
                now++;
            }
            else//操作3,两个单点查询
            {
                scanf("%d %d",&l,&r);
                ll ans=((fun(now+1,r)-fun(now+1,l-1))%mod+mod)%mod;
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

 

你可能感兴趣的:(组合数学)