2962: 序列操作

题目链接

题目大意:有一个长度为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的值

题解:维护取反标记和加标记,取反优先级高于加

对于操作3,每个线段树节点维护一个数组sum[20],分别表示选1-20个数的积的和,
为了方便,令sum[x]=1,手(看)玩小的(题解)后得到合并式
rt.sum[i]=j=0ils.sum[j]rs.sum[ij] r t . s u m [ i ] = ∑ j = 0 i l s . s u m [ j ] ∗ r s . s u m [ i − j ]
大概就是卷积……

虽然已经确定了标记下放的顺序,但还需要想想怎么下放……

对x节点的区间取反,直接把x节点的所有编号为奇数的sum取反,偶数sum不变,因为偶数里的全是偶数项,负号抵消……

对x节点的区间加,设有三个数
(a,b,c),(a+v)(b+v)(c+v)=abc+v(ab+ac+bc)+v2(a+b+c)+v3 ( a , b , c ) , ( a + v ) ∗ ( b + v ) ∗ ( c + v ) = a b c + v ( a b + a c + b c ) + v 2 ( a + b + c ) + v 3

扩展后得到   sum[i]=j=1i(sum[ij]Cjleni+jxj)     s u m [ i ] = ∑ j = 1 i ( s u m [ i − j ] ∗ C l e n − i + j j ∗ x j )

然后就可做了……………………

时间复杂度 O(c2nlogn) O ( c 2 n l o g n )

我在预处理组合数的时候没有输入n……因为这个de了三天bug……

我的收获:丧病的推式子……线段树维护奇 <script type="math/tex" id="MathJax-Element-65">♂</script>怪的东西

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define P 19940417
#define M 50005
#define ll long long
#define ls x<<1
#define rs x<<1|1
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
#define root 1,n,1

int n,m,a[M];
int len[M<<2];
ll add[M<<2],c[M][21];
bool rev[M<<2];

struct tree{ll p[21];}s[M<<2];

inline void A(ll &x,ll y){x+=y;if(x>=P) x-=P;}

tree operator +(tree a,tree b){
    tree c;c.p[0]=1;
    for(int i=1;i<=20;i++){
        c.p[i]=0;
        for(int j=0;j<=i;j++) 
            A(c.p[i],a.p[j]*b.p[i-j]%P);
    }
    return c;
}

void ins(int x,int v){
    A(add[x],v);
     for(int i=20;i>=1;i--){
        ll y=v;
        for(int j=1;j<=i;j++,y=y*v%P)
             A(s[x].p[i],y*s[x].p[i-j]%P*c[len[x]-i+j][j]%P);
    }
}

void turn(int x){
    rev[x]^=1;if(add[x]) add[x]=P-add[x];
    for(int i=1;i<=20;i+=2) if(s[x].p[i]) s[x].p[i]=P-s[x].p[i];
}

void pushdown(int x){
    if(rev[x]) turn(ls),turn(rs),rev[x]=0;
    if(add[x]) ins(ls,add[x]),ins(rs,add[x]),add[x]=0;
}

void build(int l,int r,int x)
{
    len[x]=r-l+1,s[x].p[0]=1;
    if (l==r){s[x].p[1]=a[l]%P;return ;}
    int m=(l+r)>>1;
    build(lson),build(rson); 
    s[x]=s[ls]+s[rs];
}

void rever(int L,int R,int l,int r,int x)
{
    if(L<=l&&r<=R){turn(x);return ;}
    pushdown(x);
    int m=(l+r)>>1;
    if(L<=m) rever(L,R,lson);
    if(R>m) rever(L,R,rson);
    s[x]=s[ls]+s[rs];
}

void updata(int L,int R,int v,int l,int r,int x)
{
    if(L<=l&&r<=R){ins(x,v);return ;}
    pushdown(x);
    int m=(l+r)>>1;
    if(L<=m) updata(L,R,v,lson);
    if(R>m) updata(L,R,v,rson);
    s[x]=s[ls]+s[rs];
}

tree query(int L,int R,int z,int l,int r,int x)
{
    if(L<=l&&r<=R) return s[x];
    pushdown(x);
    int m=(l+r)>>1;
    if(L<=m&&R>m) return query(L,R,z,lson)+query(L,R,z,rson);
    if(L<=m) return query(L,R,z,lson); 
    if(R>m) return query(L,R,z,rson);
}

void prec()
{
    for(int i=0;i<=n;i++){
        c[i][0]=1;
        for(int j=1;j<=i&&j<=20;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
    }
} 

void work()
{
    char opt[3];int x,y,z;
    while(m--){
        scanf("%s%d%d",opt,&x,&y);
        if(opt[0]=='I') scanf("%d",&z),z=(z+P)%P,updata(x,y,z,root);
        else if(opt[0]=='R') rever(x,y,root);
        else scanf("%d",&z),printf("%lld\n",query(x,y,z,root).p[z]);
    }
}

void init()
{
    cin>>n>>m;
    prec();
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(root);
}

int main()
{
    init(); 
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值