传送门

快省选了从数论回来练练数据结构,然后被安利去做了这道毒瘤题。

看到$3​$操作。

之后看到$c$的范围只有$20$,明显是暴力维护$20$种答案。

又到了欢乐的推式子时间:

设$f_p(i)​$为线段树$p​$节点代表区间选$i​$个数相乘所有方案得到的和,$ls(p)、rs(p)​$为$p​$的左右儿子。

合并

  • 对于一个$f_p(i)$,$ls(p)$和$rs(p)$都可以提供各自的$f_{ls(p)}(i)$和$f_{rs(p)}(i)$。

  • 然后就是两个儿子交起来的贡献,可以理解为把两个儿子拼起来,为$\sum\limits_{j=1}^{i-1}f_{ls(p)}(j)f_{rs(p)}(i-j)​$。

总复杂度为$O(20^2)​$

相反数

区间加太麻烦了先说取反。

容易想到,$i​$为奇数,取反会使$f_p(i)​$也取反;$i​$为偶数则不变。

复杂度为$O(20)​$

区间加

比如说$p$有四个数$a,b,c,d$,$f_p(3)$就有$a*b*c,a*b*d,a*c*d,b*c*d$

然后统一加上$k$,再手推一下新的答案,为$4k^3+3k^2(a+b+c)+2k(ab+ac+bc)+abc$

我们可以猜想更新后答案变为$\sum\limits_{j=0}^{i}a_jk^{i-j}f_p(j)​$,$a_j​$为系数。

问题就变成了求$a_j$。以$a_2$为例,因为$f_p(2)$包含的每种组合的系数都是$a_2$的,所以只要知道任意一项的系数就行了,于是来推一下$a*b$的系数。

$a_2$实际上就是$f_p(3)$所有组合中,可以提取出$a*b$的数量。我们可以用组合数来推:

$p$有$4$个数,选了$2$个数$a,b$后还有$4-2$个数可以选,现在要凑成$3$个数还需要$3-2$个数,即$C^{3-2}_{4-2}$

推广到所有情况,设$siz​$为区间长度。$a_j=C_{siz-j}^{i-j}​$

式子就变成了$\sum\limits_{j=0}^{i}C_{siz-j}^{i-j}k^{i-j}f_p(j)​$

组合数可以杨辉三角预处理。这个式子的复杂度就是$O(20^2*\log 20)$。

取反标记优先,取反时把加法标记一起取反。

总复杂度为$O(n\log n20^2\log 20)=O(大常数*n\log^3 n)$

$emm…$

然后按这个思路写就$A​$了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define maxn 50005

const int N = 20;
const long long mod = 19940417;

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
int n,a[maxn];
long long c[maxn][21],ans[21];
long long quickpow(long long x,long long y){
    long long an=1;
    while(y){
        if(y&1)an=an*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return an;
}
struct Segment_Tree{
    long long sum[maxn<<2][21],tag[maxn<<2];
    int siz[maxn<<2],li[maxn<<2];
    //siz为区间长度,li为min(siz,N),在build是处理出来
    bool rev[maxn<<2];
    //取反标记
#define ls(x) x<<1
#define rs(x) x<<1|1
    inline void update(int node){
        for(register int i=1;i<=li[node];++i){
            sum[node][i]=(sum[ls(node)][i]+sum[rs(node)][i])%mod;
            for(register int j=1;j<i;++j)
                sum[node][i]=(sum[node][i]+sum[ls(node)][j]*sum[rs(node)][i-j])%mod;
        }
    }    
    inline void datadown(int node,long long d){
        for(register int i=li[node];i;--i)
            for(register int j=0;j<i;++j)
                sum[node][i]=(sum[node][i]+sum[node][j]*c[siz[node]-j][i-j]%mod*quickpow(d,i-j))%mod;
        tag[node]=(tag[node]+d)%mod;
    }//区间加
    inline void reverdown(int node){
        for(register int i=1;i<=li[node];i+=2)
            sum[node][i]=-sum[node][i];//奇数取反
        rev[node]^=1,tag[node]=-tag[node];//加法标记一并取反
    }//取反
    inline void pushdown(int node){
        if(rev[node]){
            reverdown(ls(node)),reverdown(rs(node));
            rev[node]=false;
        }
        if(tag[node]){
            datadown(ls(node),tag[node]);
            datadown(rs(node),tag[node]);
            tag[node]=0;
        }
    }
    void build(int l,int r,int node){
        siz[node]=r-l+1,li[node]=min(r-l+1,N),sum[node][0]=1;
        if(l==r){
            sum[node][1]=a[l];
            return;
        }
        int mid=l+r>>1;
        build(l,mid,ls(node));
        build(mid+1,r,rs(node));
        update(node);
    }
    void add(int L,int R,int l,int r,int node,long long d){
        if(L<=l&&R>=r){
            datadown(node,d);
            return;
        }
        pushdown(node);
        int mid=l+r>>1;
        if(L<=mid)add(L,R,l,mid,ls(node),d);
        if(R>mid)add(L,R,mid+1,r,rs(node),d);
        update(node);
    }
    void reverse(int L,int R,int l,int r,int node){
        if(L<=l&&R>=r){
            reverdown(node);
            return;
        }
        pushdown(node);
        int mid=l+r>>1;
        if(L<=mid)reverse(L,R,l,mid,ls(node));
        if(R>mid)reverse(L,R,mid+1,r,rs(node));
        update(node);
    }
    void ask(int L,int R,int l,int r,int node,int c){
        if(L<=l&&R>=r){
            for(register int i=c;i;--i){
                ans[i]=(ans[i]+sum[node][i])%mod;
                for(register int j=1;j<i;++j)
                    ans[i]=(ans[i]+ans[j]*sum[node][i-j])%mod;
            }
            return;
        }
        pushdown(node);
        int mid=l+r>>1;
        if(L<=mid)ask(L,R,l,mid,ls(node),c);
        if(R>mid)ask(L,R,mid+1,r,rs(node),c);
    }
}st;
void Get_C(){
    c[0][0]=1;
    for(register int i=1;i<=n;++i){
        c[i][0]=1;
        for(register int j=1;j<=N;++j)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    }
}//预处理组合数
int main(){
    n=read();
    int m=read();
    Get_C();    
    for(register int i=1;i<=n;++i)
        a[i]=read();
    st.build(1,n,1);
    char s[2];
    int l,r,d;
    while(m--){
        scanf("%s",s);
        l=read(),r=read();
        if(s[0]=='R')st.reverse(l,r,1,n,1);
        else {
            d=read();
            if(s[0]=='I')st.add(l,r,1,n,1,d);
            else {
                memset(ans,0,sizeof ans);
                st.ask(l,r,1,n,1,d);
                printf("%lld\n",(ans[d]%mod+mod)%mod);
                //负数要模成正数输出
            }
        }
    }
}