zoukankan      html  css  js  c++  java
  • Luogu P4247 [清华集训2012]序列操作

    题意

    给定一个长度为 (n) 的序列 (a)(q) 次操作,每次操作形如以下三种:

    • I a b c,表示将 ([a,b]) 内的元素加 (c)

    • R a b,表示将 ([a,b]) 内的元素变成相反数。

    • Q a b c,表示在 ([a,b]) 内所有选出 (c) 个元素的乘积之和。

    对于每一个询问操作,输出答案对 (19940417) 取模的值。

    ( exttt{Data Range:}1leq nleq qleq 50000)

    题解

    好题啊。

    看到这种题马上想到用线段树维护区间内 ((1+a_ix)) 的乘积,然后答案就是取一项。

    考虑如何打懒标记。对于相反数的话,设 (F(x)=prodlimits_{i=1}^{n}(1+a_ix)),那么有

    [prodlimits_{i=1}^{n}(1-a_ix)=sumlimits_{i=0}^{n}(-1)^i[x^i]F(x) ]

    这个结论大概可以手玩一下,所以说打懒标记的时候只需要奇次项取相反数即可。

    接下来是区间加这个大毒瘤,还是考虑懒标记的打法。

    接下来为了方便我们用 (S_k) 表示 (a_1,a_2,cdots a_n) 中取 (k) 项的所有乘积之和,那么有

    [[1+(a_1+c)x][1+(a_2+c)x][1+(a_3+c)x]=1+(S_1+3cx)+(S_2+2S_1c+3c^2)x^2+(S_3+S_2c+S_1c^2+c^3)x^3 ]

    注意到我们可以枚举一下多项式的 (i) 次项系数和这一项系数里面是取 (j) 项的所有方案数的乘积之和,那么我们得到当前枚举的 (i,j) 对应的 (S_j) 的系数为:

    [frac{inom{n}{i}inom{i}{j}c^{i-j}}{inom{n}{j}} ]

    这里有一个组合意义的解释:首先将 ((a_i+c)x) 看做整体,从 (n) 个中选出 (i) 个构成 ((a_1+c)(a_2+c)cdots(a_i+c)x^i) 这一项。接下来在求出这一项中对应的 (c^{i-j}) 的系数,也就是选了 (j)(a)(i-j)(c)。最后,一共有 (inom{n}{j}) 种方式从这些 (a_i) 中出选 (j) 个,而每种方式被计算的次数都是相等的,于是得除掉,就得到这个东西被算了几次,也就是 (S_j) 的系数。

    利用一些基本的恒等式我们有

    [frac{inom{n}{i}inom{i}{j}c^{i-j}}{inom{n}{j}}=inom{n-j}{i-j}c^{i-j} ]

    于是枚举一下这个东西就没了,时间复杂度 (O(c^2nlog n))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef int ll;
    typedef long long int li;
    const ll MAXN=5e4+51,MOD=19940417; 
    struct SegmentTree{
        ll l,r,tag,tag2;
        ll f[21];
    };
    SegmentTree tree[MAXN<<2];
    ll n,qcnt,l,r,u;
    char op;
    ll x[MAXN],pw[MAXN],binom[MAXN][21],g[21];
    inline ll read()
    {
        register ll num=0,neg=1;
        register char ch=getchar();
        while(!isdigit(ch)&&ch!='-')
        {
            ch=getchar();
        }
        if(ch=='-')
        {
            neg=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            num=(num<<3)+(num<<1)+(ch-'0');
            ch=getchar();
        }
        return num*neg;
    }
    inline void conv(ll *f,ll *g,ll *res)
    {
        li cur;
        for(register int i=0;i<=20;i++)
        {
            cur=0;
            for(register int j=0;j<=i;j++)
            {
                cur+=(li)f[j]*g[i-j]%MOD;
            }
            res[i]=cur%MOD;
        }
    }
    #define ls node<<1
    #define rs (node<<1)|1
    inline void update(ll node)
    {
        conv(tree[ls].f,tree[rs].f,tree[node].f);
    }
    inline void create(ll l,ll r,ll node)
    {
        tree[node]=(SegmentTree){l,r,0,1,{1}};
        if(l==r)
        {
            return (void)(tree[node].f[1]=x[l]);
        }
        ll mid=(l+r)>>1;
        create(l,mid,ls),create(mid+1,r,rs),update(node);
    }
    inline void spread(ll node)
    {
        if(tree[node].tag2!=1)
        {
            for(register int i=1;i<=20;i+=2)
            {
                tree[ls].f[i]=(MOD-tree[ls].f[i])%MOD;
                tree[rs].f[i]=(MOD-tree[rs].f[i])%MOD;
            }
            tree[ls].tag2*=-1,tree[rs].tag2*=-1,tree[node].tag2=1;
            tree[ls].tag=(MOD-tree[ls].tag)%MOD;
            tree[rs].tag=(MOD-tree[rs].tag)%MOD;
        }
        if(tree[node].tag)
        {
            ll lenl=tree[ls].r-tree[ls].l+1,lenr=tree[rs].r-tree[rs].l+1;
            li curl,curr;
            for(register int i=1;i<=20;i++)
            {
                pw[i]=(li)pw[i-1]*tree[node].tag%MOD;
            }
            for(register int i=20;i;i--)
            {
                curl=curr=0;
                for(register int j=0;j<i;j++)
                {
                    curl+=(li)tree[ls].f[j]*pw[i-j]%MOD*binom[lenl-j][i-j]%MOD;
                    curr+=(li)tree[rs].f[j]*pw[i-j]%MOD*binom[lenr-j][i-j]%MOD;
                }
                tree[ls].f[i]=(tree[ls].f[i]+curl)%MOD;
                tree[rs].f[i]=(tree[rs].f[i]+curr)%MOD;
            }
            tree[ls].tag+=tree[node].tag,tree[rs].tag+=tree[node].tag;
            tree[ls].tag%=MOD,tree[rs].tag%=MOD,tree[node].tag=0;
        }
    }
    inline void add(ll l,ll r,ll val,ll node)
    {
        if(l<=tree[node].l&&r>=tree[node].r)
        {
            ll len=tree[node].r-tree[node].l+1;
            li cur=0;
            for(register int i=1;i<=20;i++)
            {
                pw[i]=(li)pw[i-1]*val%MOD;
            }
            for(register int i=20;i;i--)
            {
                cur=0;
                for(register int j=0;j<i;j++)
                {
                    cur+=(li)tree[node].f[j]*pw[i-j]%MOD*binom[len-j][i-j]%MOD;
                }
                tree[node].f[i]=(tree[node].f[i]+cur)%MOD;
            }
            return (void)(tree[node].tag=(tree[node].tag+val)%MOD);
        }
        ll mid=(tree[node].l+tree[node].r)>>1;
        spread(node);
        l<=mid?add(l,r,val,ls):(void)1,r>mid?add(l,r,val,rs):(void)1;
        update(node);
    }
    inline void mul(ll l,ll r,ll node)
    {
        if(l<=tree[node].l&&r>=tree[node].r)
        {
            for(register int i=1;i<=20;i+=2)
            {
                tree[node].f[i]=(MOD-tree[node].f[i])%MOD;
            }
            tree[node].tag=(MOD-tree[node].tag)%MOD;
            return (void)(tree[node].tag2*=-1);
        }
        ll mid=(tree[node].l+tree[node].r)>>1;
        spread(node);
        l<=mid?mul(l,r,ls):(void)1,r>mid?mul(l,r,rs):(void)1,update(node);
    }
    inline SegmentTree query(ll l,ll r,ll node)
    {
        SegmentTree res;
        if(l<=tree[node].l&&r>=tree[node].r)
        {
            return tree[node];
        }    
        ll mid=(tree[node].l+tree[node].r)>>1;
        spread(node),conv(l<=mid?query(l,r,ls).f:g,r>mid?query(l,r,rs).f:g,res.f);
        return res;
    }
    int main()
    {
        n=read(),qcnt=read(),binom[0][0]=pw[0]=g[0]=1;
        for(register int i=1;i<=n;i++)
        {
            x[i]=(read()%MOD+MOD)%MOD;
        }
        for(register int i=1;i<=n;i++)
        {
            binom[i][0]=1;
            for(register int j=1;j<=20;j++)
            {
                binom[i][j]=(binom[i-1][j]+binom[i-1][j-1])%MOD;
            }
        }
        create(1,n,1);
        for(register int i=0;i<qcnt;i++)
        {
            cin>>op,l=read(),r=read();
            if(op=='I')
            {
                u=(read()%MOD+MOD)%MOD,add(l,r,u,1);
            }
            if(op=='R')
            {
                mul(l,r,1);
            }
            if(op=='Q')
            {
                u=read();
                printf("%d
    ",query(l,r,1).f[u]);
            }
        }
    }
    
  • 相关阅读:
    导入数据库的命令
    截取字符串
    用decode函数实现行变列
    初始库存入库相关知识
    客户欠款余额账
    存货管理
    创建临时表(转)
    求余额
    学习浪潮系统
    oracle number类型
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13669908.html
Copyright © 2011-2022 走看看