1. 程式人生 > 實用技巧 >Luogu P4247 [清華集訓2012]序列操作

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\) 取模的值。

\(\texttt{Data Range:}1\leq n\leq q\leq 50000\)

題解

好題啊。

看到這種題馬上想到用線段樹維護區間內 \((1+a_ix)\) 的乘積,然後答案就是取一項。

考慮如何打懶標記。對於相反數的話,設 \(F(x)=\prod\limits_{i=1}^{n}(1+a_ix)\),那麼有

\[\prod\limits_{i=1}^{n}(1-a_ix)=\sum\limits_{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{\binom{n}{i}\binom{i}{j}c^{i-j}}{\binom{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\)。最後,每個可能的方案都會被算 \(\binom{n}{j}\) 次,於是得除掉。

利用一些基本的恆等式我們有

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

於是列舉一下這個東西就沒了,時間複雜度 \(O(c^2n\log 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\n",query(l,r,1).f[u]);
        }
    }
}