Rikka with Prefix Sum(组合数想法题)

原题:牛客网暑期ACM多校训练营(第十场)

题意:

n的数组m次操作

  1. 1 L R W : L~R的数加上W
  2. 2 : 原数组变成这个数组的前缀数组
  3. 3 L R : 求L~R的区间和

输入描述:
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.

解析:

因为3操作的数量只有500,所以时间的分配应该是num(1,2)*num(3)即O(m*500)

分析每个操作一对答案的贡献:

{ 1 1 1 0 0 0 1 2 3 3 3 3 1 3 6 9 12 15 1 4 10 19 31 46 . . . }

首先,这个矩阵的 q + 1 a (设为 A ( q , a ) )代表操作二 q 次后操作一对L+a-1的位置的贡献(L为这次操作一的左边界)

显然,对于L~R的区间内的数,有 A ( q , a ) = C q 1 + a q

显然,R+1后面的数计算起来非常麻烦(我推了半天推出来了结果100%TLE)

那么我们可以换一种思路,把 { 1 1 1 0 0 0 } 变成 { 1 1 1 1 1 1 } + { 0 0 0 1 1 1 } ,就可以不用考虑后面的复杂情况了


然后就是对一个区间的贡献和了

i = 1 R A ( q , i ) = C q q + C q + 1 q . . . C q + R 1 q = C q + 1 + R 1 q + 1

所以就做出来了,对于每一个操作三,遍历之前的所有操作一(当然要记下到那个时候的操作2的次数,再用总次数减去那个次数,就是这次操作一到操作三的操作二的次数)

代码:


D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
const int N=200000;
const F pi=acos(-1);
const D mod=998244353;
const F _e=2.718281828459045;

/*快速幂*/
D swift(D a,D b){
    D ans=1ll;
    while(b){
        if(b%2)ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }return ans;
}

D inv(D a){return swift(a,mod-2);}//费马小定理


/*预处理阶乘*/
D fac[N+9];
D inv_fac[N+9];
void init_fac(){
    fac[0]=fac[1]=1ll;
    for(int i=2;i<=N;i++)fac[i]=fac[i-1]*i%mod;
    //预处理阶乘逆元
    inv_fac[N]=swift(fac[N],mod-2);//费马小定理求 N!的逆元
    for(D i=N-1;i>=1;i--) inv_fac[i]=(inv_fac[i+1]*(i+1))%mod;
    inv_fac[0]=1;
}



D A(D a,D b){//排列数 a下
    if(b>a||b<0)return 0;
    return fac[a]*inv_fac[a-b]%mod;
}
D C(D a,D b){//组合数 a下
    if(b>a||b<0)return 0;
    return fac[a]*inv_fac[a-b]%mod*inv_fac[b]%mod;
}


D arr[200009];
int preq[200009];
int numq;int now;
int l[200009],r[200009];D v[200009];


D Csum(D l,D r,D v,D q){
    if(l>r)return 0;
    D n=r-l+1;
    D ans=C(q+n-1,q)*v%mod;
    ans=(ans+mod)%mod;
    return ans;
}

D query(D R){
    D ans=0;
    for(int i=1;i<=now;i++){
        int q=numq-preq[i]+1;//因为组合数的和是q变成q+1,所以在这里先加上
        if(l[i]>R)continue;
        if(r[i]<=R)
            ans=(ans+Csum(l[i],R,v[i],q))%mod,//C(q,q)+C(q+1,q)+...C(q+n1-1,q)
            ans=(ans+Csum(r[i]+1,R,-v[i],q)+mod)%mod;
        else
            ans=(ans+Csum(l[i],R,v[i],q)+mod)%mod;
    }
    return ans;
}

void solve(int L,int R){
    printf("%lld\n",(query(R)-query(L-1)+mod)%mod);
}

int main(){
    init_fac();
    int t=read();
    while(t--){
        now=0;
        numq=0;
        int n=read(),m=read();
        while(m--){
            int f=read();
            if(f==1){
                l[++now]=read(),r[now]=read();v[now]=read();
                preq[now]=numq;
            }
            else if(f==2){
                numq++;
            }
            else{
                int l=read(),r=read();
                solve(l,r);
            }
        }
    }
}






猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81840643
今日推荐