[清华集训]序列操作

写了这么多数据结构题,一半左右是线段树
线段树真是优秀啊

题意

有一个长度为n的序列,有三个操作:

1.I a b c表示将\([a,b]\)这一段区间的元素集体增加\(c\)
2.R a b表示将\([a,b]\)区间内所有元素变成相反数
3.Q a b c表示询问\([a,b]\)这一段区间中选择\(c\)个数相乘的所有方案的和\(\mod 19940417\)的值(\(1 \leq c \leq 20\))

首先考虑维护哪些信息
显然需要维护一个数组表示答案

然后考虑合并
显然
\(f[rt][n]=f[ls][n]+f[rs][n]+\sum_{i=1}^{n-1}{f[ls][i]*f[rs][n-i]}\)

再考虑维护哪些标记
区间加法,区间反转

考虑标记的维护
显然先维护反转标记后维护加法标记

最后考虑修改答案
区间反转只需把乘奇数个数的答案反转
区间加法类似二项式定理可以推出(好吧其实和二项式定理没有什么关系)
\(f[rt][n]=f[rt][n]+ \sum_{i=1}^{n-1}{f[rt][j]*C_{size-j}^{i-j}* k^i } + C_{size}^i * k^n\)
因为前面的值会影响后面的,所以从后往前更新

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}

const int N=1e5+7;
const int p=19940417;

ll C[N][21];

struct Seg{
    bool tag1;
    ll tag2,a[21];
    int siz;
    
    Seg(){
        tag1=0;
        tag2=0;
        memset(a,0,sizeof(a));
        siz=1;
    }
    
    inline const ll& operator [](const int &x)const{
        return a[x];
    }
    
    inline ll& operator [](const int &x){
        return a[x];
    }
    
    inline void reverse(){
    	tag1^=1;
    	if(tag2)tag2=p-tag2;
    	for(int i=1;i<20;i+=2)if(a[i])a[i]=p-a[i];
    }
    
    inline void increase(int x){
    	(tag2+=x)%=p;
        for(int i=min(20,siz);i;--i){
            ll Tmp=x;
            for(int j=i-1;j;--j,(Tmp*=x)%=p){
                (a[i]+=Tmp*a[j]%p*C[siz-j][i-j]%p)%=p;
            }
            (a[i]+=C[siz][i]*Tmp)%=p;
        }
    }
    	
}tr[N<<2];

inline Seg operator + (const Seg &a,const Seg &b){
    Seg ret;
    ret[0]=1;
    ret.siz=a.siz+b.siz;
    for(int i=1;i<=min(ret.siz,20);++i){
        ret[i]=(a[i]+b[i])%p;
        for(int j=1;j<i;++j){
            (ret[i]+=a[j]*b[i-j])%=p;
        }
    }
    return ret;
}

inline void update(int rt){
    tr[rt]=tr[ls]+tr[rs];
}

inline void pushdown(int rt){
    if(tr[rt].tag1){
        tr[ls].reverse();
        tr[rs].reverse();
        tr[rt].tag1=0;
    }
    if(tr[rt].tag2){
        tr[ls].increase(tr[rt].tag2);
        tr[rs].increase(tr[rt].tag2);
        tr[rt].tag2=0;
    }
}

int a[N];

void build(int rt,int l,int r){
    if(l==r){
        tr[rt][0]=1;
        tr[rt][1]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    update(rt);
}

void reverse(int rt,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        tr[rt].reverse();
        return;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(x<=mid)reverse(ls,l,mid,x,y);
    if(y>mid)reverse(rs,mid+1,r,x,y);
    update(rt);
}

void increase(int rt,int l,int r,int x,int y,int v){
    if(x<=l&&r<=y){
        tr[rt].increase(v);
        return;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(x<=mid)increase(ls,l,mid,x,y,v);
    if(y>mid)increase(rs,mid+1,r,x,y,v);
    update(rt);
}

Seg query(int rt,int l,int r,int x,int y){
    if(x<=l&&r<=y)return tr[rt];
    pushdown(rt);
    int mid=(l+r)>>1;
    if(y<=mid)return query(ls,l,mid,x,y);
    else if(x>mid)return query(rs,mid+1,r,x,y);
    else return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
}

inline void pre(int n){
    for(int i=0;i<=n;++i){
        C[i][0]=C[i][i]=1;
        for(int j=1;j<=min(i-1,20);++j){
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
        }
    }
}

int main(){
    int n,q;r(n),r(q);pre(n);
    for(int i=1;i<=n;++i)r(a[i]),a[i]=(a[i]%p+p)%p;
    build(1,1,n);
    while(q--){
        char opt[5];scanf("%s",opt);
        int l,r,x;r(l),r(r);
        switch(opt[0]){
            case 'I':{r(x);x=(x%p+p)%p;increase(1,1,n,l,r,x);break;}
            case 'R':{reverse(1,1,n,l,r);break;}
            case 'Q':{r(x),printf("%lld\n",query(1,1,n,l,r)[x]);break;}
        }
    }
}

posted @ 2018-10-23 22:16  NamelessOIer  阅读(249)  评论(0编辑  收藏  举报