bzoj2962 序列操作 线段树

33 篇文章 0 订阅

题意:一段序列,三种操作:
1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反数,3.Q a b c表示询问[a,b]这一段区间中选择c个数相乘的所有方案的和mod 19940417的值。

一开始没看到k<=20,觉得不可做,后来一看,哦这不就水了很多了。。
操作3的话维护f[i]表示当前区间选择i个的答案,那么n^2更新,明显有:
f[i]=f[j]f[ij];(j<i)
取负也很简单,只有奇数的时候才会有影响。
问题就是区间加有些难以处理。
假设加上x,区间长度为len,那么就是类似于 (.....+x)(.....+x)(......+x) 这样的一个东西。
那么我们枚举x有j个,答案就是 j=0ixjf[ij]Cjleni+j
后面那个组合数应该挺好理解,i-j个数字被确定,剩下的len-i+j个中选出j个x。
3000多B让我有点爽。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=50005;
const int mo=19940417;
typedef long long ll;
int n,m;
struct node
{
    int l,r,lazy1,lazy2;
    ll f[21];;
}t[N*5]; 
int a[N];
int c[N][30];
inline void update(int x)
{
    fo(i,0,20)
    {
        t[x].f[i]=0;
        fo(j,0,i)t[x].f[i]=(t[x].f[i]+t[x<<1].f[j]*t[x<<1|1].f[i-j])%mo;
    }
}
inline void push1(int x)
{
    for(int i=1;i<=20;i+=2)t[x].f[i]*=-1;
}
inline void push2(int x,int l,int r,int v)
{
    int len=r-l+1;
    fd(i,min(len,20),1)
    {
        ll tmp=0;int mi=1;
        fo(j,0,i)tmp=(tmp+1ll*mi*t[x].f[i-j]%mo*c[len-i+j][j])%mo,mi=1ll*mi*v%mo;
        t[x].f[i]=tmp;
    }
}
inline void build(int x,int l,int r)
{
    t[x].l=l,t[x].r=r;t[x].lazy1=1;
    if (l==r)
    {
        t[x].f[1]=a[l];
        t[x].f[0]=1; 
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    update(x);
}
inline void pushdown(int x,int l,int r)
{
    if (l==r)return;
    int mid=(l+r)>>1,w=t[x].lazy2;
    if (t[x].lazy1==-1)
    {
        push1(x<<1),push1(x<<1|1);
        t[x<<1].lazy1*=-1,t[x<<1|1].lazy1*=-1;
        t[x<<1].lazy2*=-1,t[x<<1|1].lazy2*=-1;
        t[x].lazy1=1;
    }
    if (w)
    {
        push2(x<<1,l,mid,w),push2(x<<1|1,mid+1,r,w);
        t[x<<1].lazy2=(t[x<<1].lazy2+w)%mo;
        t[x<<1|1].lazy2=(t[x<<1|1].lazy2+w)%mo;
        t[x].lazy2=0;
    }
}
inline void add(int x,int l,int r,int v)
{
    if (l>r)return;
    pushdown(x,t[x].l,t[x].r);
    if (t[x].l==l&&t[x].r==r)
    {
        push2(x,t[x].l,t[x].r,v);
        t[x].lazy2=(t[x].lazy2+v)%mo;
        return;
    }
    int mid=(t[x].l+t[x].r)>>1;
    add(x<<1,l,min(r,mid),v);
    add(x<<1|1,max(l,mid+1),r,v);
    update(x);
}
inline void oppo(int x,int l,int r)
{
    if (l>r)return;
    pushdown(x,t[x].l,t[x].r);
    if (t[x].l==l&&t[x].r==r)
    {
        push1(x);
        t[x].lazy1*=-1;
        return;
    }
    int mid=(t[x].l+t[x].r)>>1;
    oppo(x<<1,l,min(r,mid));
    oppo(x<<1|1,max(l,mid+1),r);
    update(x);
}
inline node query(int x,int l,int r,int v)
{
    pushdown(x,t[x].l,t[x].r);
    if (t[x].l==l&&t[x].r==r)return t[x];
    int mid=(t[x].l+t[x].r)>>1;
    if (r<=mid)return query(x<<1,l,r,v);
    else if (l>mid)return query(x<<1|1,l,r,v);
    else
    {
        node a=query(x<<1,l,mid,v),
        b=query(x<<1|1,mid+1,r,v),ans;
        fo(i,0,v)
        {
            ans.f[i]=0;
            fo(j,0,i)
                ans.f[i]=(ans.f[i]+a.f[j]*b.f[i-j]%mo)%mo;
        }
        return ans;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&a[i]);
    build(1,1,n);
    c[0][0]=1;
    fo(i,1,n)
    {
        c[i][0]=1;
        fo(j,1,20)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    }
    fo(i,1,m)
    {
        char ch[2];
        int l,r,v;
        scanf("%s",ch);
        if (ch[0]=='I')scanf("%d%d%d",&l,&r,&v),add(1,l,r,v%mo);
        else if (ch[0]=='R')scanf("%d%d",&l,&r),oppo(1,l,r);
        else
        {
            scanf("%d%d%d",&l,&r,&v);
            node x=query(1,l,r,v);
            printf("%lld\n",(x.f[v]+mo)%mo);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值