[BZOJ2962]序列操作:线段树+组合数学

分析:

线段树维护序列信息,需要注意的几个点:
1、标记的更新顺序:先更新RevTag,再更新AddTag。
2、作区间加法时f[i]=∑(j=0 to i)x^j*f[i-j]*C[len-(i-j)][j],证明有些像二项式定理。
3、pushup()时由于k较小,直接暴力卷积即可,记得先把f数组清零。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
typedef long long LL;

inline LL read(){
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

const int MAXN=50005;
const LL MOD=19940417;
int n,q,ql,qr;
LL c[MAXN][21],seq[MAXN],kk;
struct Node{
    LL f[21],atag;
    int rtag;
    Node(){
        memset(f,0,sizeof f);
        atag=rtag=0;
    }
}a[MAXN<<2];

inline void pre_process(){
    c[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=20;j++){
            c[i][j]=c[i-1][j];
            if(j) c[i][j]=(c[i][j]+c[i-1][j-1])%MOD;
        }
}

#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
#define len (r-l+1)
inline void pushup(int o,int l,int r){
    for(int i=0;i<=len&&i<=20;i++){
        a[o].f[i]=0;
        for(int j=0;j<=((len+1)>>1)&&j<=i;j++)
            a[o].f[i]=(a[o].f[i]+a[lc].f[j]*a[rc].f[i-j])%MOD;
    }
}

inline void pushr(int o,int l,int r){
    for(int i=1;i<=len&&i<=20;i+=2) a[o].f[i]=-a[o].f[i];
    a[o].atag=-a[o].atag;
    a[o].rtag^=1;
}

inline void pusha(int o,int l,int r,LL kkk){
    for(int i=std::min(len,20);i+1;i--){
        LL temp=1;
        for(int j=0;j<=i;j++){
            if(j==0){
                temp=temp*kkk%MOD;
                continue;
            }
            a[o].f[i]=(a[o].f[i]+temp*a[o].f[i-j]%MOD*c[len-i+j][j])%MOD;
            temp=temp*kkk%MOD;
        }
    }
    a[o].atag=(a[o].atag+kkk)%MOD;
}

inline void pushdown(int o,int l,int r){
    if(a[o].rtag){
        pushr(lc,l,mid);
        pushr(rc,mid+1,r);
        a[o].rtag=0;
    }
    if(a[o].atag){
        pusha(lc,l,mid,a[o].atag);
        pusha(rc,mid+1,r,a[o].atag);
        a[o].atag=0;
    }
}

void build(int o,int l,int r){
    if(l==r){
        a[o].f[0]=1;
        a[o].f[1]=seq[l]%MOD;
        return;
    }
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(o,l,r);
//  std::cout<<l<<" "<<r<<">>>";
//  for(int i=0;i<=20;i++)
//      std::cout<<a[o].f[i]<<" ";
//  std::cout<<std::endl;   
}

void radd(int o,int l,int r){
    if(ql<=l&&r<=qr){
        pusha(o,l,r,kk);
        return;
    }
    pushdown(o,l,r);
    if(mid>=ql) radd(lc,l,mid);
    if(mid<qr) radd(rc,mid+1,r);
    pushup(o,l,r);
}

void rrev(int o,int l,int r){
    if(ql<=l&&r<=qr){
        pushr(o,l,r);
        return;
    }
    pushdown(o,l,r);
    if(mid>=ql) rrev(lc,l,mid);
    if(mid<qr) rrev(rc,mid+1,r);
    pushup(o,l,r);
}

Node query(int o,int l,int r){
    if(ql<=l&&r<=qr) return a[o];
    pushdown(o,l,r);
    if(mid<ql) return query(rc,mid+1,r);
    else if(mid>=qr) return query(lc,l,mid);
    else{
        Node ret1=query(lc,l,mid);
        Node ret2=query(rc,mid+1,r);
        Node ret;
        for(int i=0;i<=20;i++)
            for(int j=0;j<=i;j++)
                ret.f[i]=(ret.f[i]+ret1.f[j]*ret2.f[i-j])%MOD;
        return ret;
    }
}
#undef mid
#undef lc
#undef rc
#undef len

int main(){
    n=read(),q=read();
    pre_process();
    for(int i=1;i<=n;i++) seq[i]=read();
    build(1,1,n);
    while(q--){
        char opt=getchar();
        while(!isalpha(opt)) opt=getchar();
        if(opt=='I'){
            ql=read(),qr=read(),kk=read();
            radd(1,1,n);
        }
        else if(opt=='R'){
            ql=read(),qr=read();
            rrev(1,1,n);
        }
        else{
            ql=read(),qr=read();int c=read();
            Node ans=query(1,1,n);
            printf("%lld\n",(ans.f[c]+MOD)%MOD);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9748165.html