bzoj 2962: 序列操作

Posted yuzao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2962: 序列操作相关的知识,希望对你有一定的参考价值。

Description

  有一个长度为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的值。

Solution

注意到 (c) 比较小,可以直接维护一个 (O(20^2))(DP)
即设 (f[i]) 表示选了 (i) 个数相乘的方案
用线段树维护
合并的话就是 (f[o][i]=sum_{j=0}^{i} f[ls][j]*f[rs][i-j])

考虑修改
加上 (x),其实就是把所有的 (a*b*c) 变成 ((a+x)*(b+x)*(c+x))
用展开就是:
(a*b*c+(ab+bc+ac)*x+(a+b+c)*x^2+x^3)
那么 (f[i]) 修改之后的值就是
(f[i]=sum_{j=1}^{i}f[j]*C_{len-j}^{i-j}*x^{i-j})
(len) 是区间长度,因为这个多项式是有 (len) 项的,所以要乘以组合数

对于变成相反数的操作,要注意:
不仅维护的值要变,标记也要取反

#include <bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
using namespace std;
const int N=50010,mod=19940417;
inline int gi(){
    register int str=0;register char ch=getchar();bool fg=0;
    while(ch>'9' || ch<'0'){if(ch=='-')fg=1;ch=getchar();}
    while(ch>='0' && ch<='9')str=(str<<3)+(str<<1)+ch-48,ch=getchar();
    return fg?-str:str;
}
int n,Q,c[N][25],f[25],la[N*4];bool rev[N*4];
struct data{int b[25];data(){memset(b,0,sizeof(b));b[0]=1;}}tr[N*4];
inline void priwork(){
    for(int i=0;i<=n;i++){
        c[i][0]=1;
        for(int j=min(20,i);j>=1;j--)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    }
}
inline data merge(data x,data y){
    data r;
    for(int i=1;i<=20;i++)
        for(int j=0;j<=i;j++)
            r.b[i]=(r.b[i]+1ll*x.b[j]*y.b[i-j])%mod;
    return r;
}
inline void build(int l,int r,int o){
    if(l==r){tr[o].b[1]=gi()%mod;return ;}
    int mid=(l+r)>>1;
    build(l,mid,ls);build(mid+1,r,rs);
    tr[o]=merge(tr[ls],tr[rs]);
}
inline void Rec1(int o,int t,int l,int r){
    la[o]=(la[o]+t)%mod;
    for(int len=r-l+1,i=min(len,20);i>=1;i--){
        f[i]=0;
        for(int j=i,tp=1;j>=0;j--,tp=1ll*tp*t%mod)
            f[i]=(f[i]+1ll*tr[o].b[j]*c[len-j][i-j]%mod*tp)%mod;
    }
    for(int i=min(r-l+1,20);i>=1;i--)tr[o].b[i]=f[i];
}
inline void Rec2(int o){
    rev[o]^=1;la[o]=(mod-la[o])%mod;
    for(int i=1;i<=20;i+=2)tr[o].b[i]=(mod-tr[o].b[i])%mod;
}
inline void pushdown(int o,int l,int r){
    if(rev[o])Rec2(ls),Rec2(rs),rev[o]=0;
    if(la[o]){
        int mid=(l+r)>>1;
        Rec1(ls,la[o],l,mid),Rec1(rs,la[o],mid+1,r),la[o]=0;
    }
}
inline void add(int l,int r,int o,int sa,int se,int t){
    if(sa<=l && r<=se){Rec1(o,t,l,r);return ;}
    pushdown(o,l,r);
    int mid=(l+r)>>1;
    if(se<=mid)add(l,mid,ls,sa,se,t);
    else if(sa>mid)add(mid+1,r,rs,sa,se,t);
    else add(l,mid,ls,sa,mid,t),add(mid+1,r,rs,mid+1,se,t);
    tr[o]=merge(tr[ls],tr[rs]);
}
inline void Modify(int l,int r,int o,int sa,int se){
    if(sa<=l && r<=se){Rec2(o);return ;}
    pushdown(o,l,r);
    int mid=(l+r)>>1;
    if(se<=mid)Modify(l,mid,ls,sa,se);
    else if(sa>mid)Modify(mid+1,r,rs,sa,se);
    else Modify(l,mid,ls,sa,mid),Modify(mid+1,r,rs,mid+1,se);
    tr[o]=merge(tr[ls],tr[rs]);
}
inline data qry(int l,int r,int o,int sa,int se){
    if(sa<=l && r<=se)return tr[o];
    pushdown(o,l,r);
    int mid=(l+r)>>1;data ret;
    if(se<=mid)ret=qry(l,mid,ls,sa,se);
    else if(sa>mid)ret=qry(mid+1,r,rs,sa,se);
    else ret=merge(qry(l,mid,ls,sa,mid),qry(mid+1,r,rs,mid+1,se));
    tr[o]=merge(tr[ls],tr[rs]);
    return ret;
}
int main()
{
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    cin>>n>>Q;priwork();
    build(1,n,1);
    char s[2];int l,r;
    while(Q--){
        scanf("%s%d%d",s,&l,&r);
        if(s[0]=='I')add(1,n,1,l,r,gi()%mod);
        else if(s[0]=='R')Modify(1,n,1,l,r);
        else printf("%d
",(qry(1,n,1,l,r).b[gi()]+mod)%mod);
    }
    return 0;
}

以上是关于bzoj 2962: 序列操作的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 2962: 序列操作

bzoj 2962: 序列操作

[BZOJ2962][清华集训]序列操作

bzoj2962序列操作 线段树

bzoj 2962 序列操作——线段树(卷积?)

bzoj2962 序列操作 题解