【题解】洛谷P4247[清华集训]序列操作 线段树

题目链接

题目描述

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

I a b c表示将[a,b][a,b]这一段区间的元素集体增加cc;
R a b表示将[a,b][a,b]区间内所有元素变成相反数;
Q a b c表示询问[a,b][a,b]这一段区间中选择cc个数相乘的所有方案的和\mod 19940417mod19940417的值。

输入输出格式

输入格式:
第一行两个数n, qn,q表示序列长度和操作个数。

第二行nn个非负整数,表示序列。

接下来qq行每行输入一个操作I a b c或者R a b或者Q a b c,意义如题目描述。

输出格式:
对于每个询问,输出选出cc个数相乘的所有方案的和\mod 19940417mod19940417的值。

输入输出样例

输入样例#1:
5 5
1 2 3 4 5
I 2 3 1
Q 2 4 2
R 1 5
I 1 3 -1
Q 1 5 1
输出样例#1:
40
19940397

说明

样例说明:

做完第一个操作序列变为1 3 4 4 5。

第一次询问结果为3 \times 4+3 \times 4+4 \times 4=403×4+3×4+4×4=40。

做完R操作变成-1 -3 -4 -4 -5。

做完I操作变为-2 -4 -5 -4 -5。

第二次询问结果为-2-4-5-4-5=-20−2−4−5−4−5=−20。

数据范围:

对于100%的数据,n \leq 50000, q \leq 50000n≤50000,q≤50000,初始序列的元素的绝对值\leq 109≤109,保证[a,b][a,b]是一个合法区间,I操作中|c| \leq 109∣c∣≤109,Q操作中1 \leq c \leq \min(b-a+1,20)1≤c≤min(b−a+1,20)。


线段树的操作真的多……可以维护好多东西啊。感觉好像只考线段树,还挺激动的,结果自己推一推还是推不动……学习了大佬博客,那个公式推导是真的精髓,tql。
自己要走的路还很长呢,多学学这些大佬的思路。

#include
typedef long long ll;
const int N=50007;
const int M=500007;
const int mod=19940417;
#define lc o<<1
#define rc o<<1|1
int n,q,rev[M],a[N];
ll lazy[M],c[N][21],p[21];
struct node{
    ll f[21];
}ans[M];
node merge(node x,node y)
{
    node z;
    for(int i=0;i<=20;i++)
    {
        z.f[i]=0;
        for(int j=0;j<=i;j++)z.f[i]=(z.f[i]+x.f[j]*y.f[i-j]%mod)%mod;
    }
    return z;
}
void push_up(int o)
{
    ans[o]=merge(ans[lc],ans[rc]);
}
void build(int o,int l,int r)
{
    if(l==r)
    {
        ans[o].f[0]=1;
        ans[o].f[1]=(a[l]%mod+mod)%mod;
        return;
    }
    int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);
    push_up(o);
}
void change(int o,int len,ll v)
{
    p[0]=1;
    for(int i=1;i<=20;i++)p[i]=(p[i-1]*v)%mod;//求出每一项系数 
    for(int i=20;i>=0;i--)
    for(int j=0;j*p[i-j]%mod*c[len-j][i-j]%mod)%mod;
}
void cle(int o)
{
    for(int i=0;i<=20;i++)if(i%2)ans[o].f[i]=(mod-ans[o].f[i])%mod;
}
void push_down(int o,int l,int r)
{
    if(rev[o])
    {
        rev[lc]^=1;
        lazy[lc]=(mod-lazy[lc])%mod;
        cle(lc);
        rev[rc]^=1;
        lazy[rc]=(mod-lazy[rc])%mod;
        cle(rc);
        rev[o]^=1;
    }
    if(lazy[o])
    {
        lazy[lc]=(lazy[o]+lazy[lc])%mod;
        lazy[rc]=(lazy[o]+lazy[rc])%mod;
        int mid=l+r>>1;
        change(lc,mid-l+1,lazy[o]);
        change(rc,r-mid,lazy[o]);
        lazy[o]=0;
    }
}
void update(int o,int l,int r,int nl,int nr,ll v)
{
    if(nl==l&&r==nr)
    {
        lazy[o]=(lazy[o]+v)%mod;
        change(o,r-l+1,v);
        return;
    }
    push_down(o,l,r);
    int mid=l+r>>1;
    if(nr<=mid)update(lc,l,mid,nl,nr,v);
    else if(nl>mid)update(rc,mid+1,r,nl,nr,v);
    else update(lc,l,mid,nl,mid,v),update(rc,mid+1,r,mid+1,nr,v);
    push_up(o);
}
void Rev(int o,int l,int r,int nl,int nr)
{
    if(nl==l&&r==nr)
    {
        rev[o]^=1;
        cle(o);
        lazy[o]=(mod-lazy[o])%mod;
        return;
    }
    push_down(o,l,r);
    int mid=l+r>>1;
    if(nr<=mid)Rev(lc,l,mid,nl,nr);
    else if(nl>mid)Rev(rc,mid+1,r,nl,nr);
    else Rev(lc,l,mid,nl,mid),Rev(rc,mid+1,r,mid+1,nr);
    push_up(o);
}
node query(int o,int l,int r,int nl,int nr)
{
    if(nl==l&&r==nr)return ans[o];
    push_down(o,l,r);
    int mid=l+r>>1;
    if(nr<=mid)return query(lc,l,mid,nl,nr);
    else if(nl>mid)return query(rc,mid+1,r,nl,nr);
    else return merge(query(lc,l,mid,nl,mid),query(rc,mid+1,r,mid+1,nr));
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&q);
    char op[10];int x,y;ll z;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    c[0][0]=1;
    for(int i=1;i<=n;i++)//递推求组合数 
    {
        c[i][0]=1;
        for(int j=1;j<=20;j++)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    }
    build(1,1,n);
    while(q--)
    {
        scanf("%s",op);
        if(op[0]=='I')//增加 
        {
            scanf("%d%d%lld",&x,&y,&z);
            z=(z%mod+mod)%mod;
            update(1,1,n,x,y,z);
        }
        else if(op[0]=='R')//变成相反数 
        {
            scanf("%d%d",&x,&y);
            Rev(1,1,n,x,y);
        }
        else
        {
            scanf("%d%d%lld",&x,&y,&z);
            printf("%lld\n",query(1,1,n,x,y).f[z]);
        }
    }
    return 0;
}

总结

又是线段树维护的奇奇怪怪的东西

你可能感兴趣的:(清华集训,洛谷,线段树)