欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[题解] 送分 二项式展开

程序员文章站 2022-06-06 22:20:35
...

题目描述

[题解] 送分 二项式展开

数据范围:n,m105, ai,x<998244353n,m \leq 10^5,~ a_i,x < 998244353

题解

看到这种异或统计答案的题可以立马想到二进制拆分的套路。

我们可以对每一个二进制位分别讨论:

aa中所有元素第bitbit位为11的个数有num1num1个,为00的有num0num0个。

那么有贡献时当且仅当bitbit位为11的被选了奇数次,为00的被选任意次。

设第bitbit位为11的有ii个被选,为00的有jj个被选。

那么贡献为:

Cnum1i×Cnum0j×xi+j    (i) C_{num1}^{i} \times C_{num0}^{j} \times x^{i+j} ~~~~(i为奇数)

把这个式子变一下为:

(Cnum1i×xi)×(Cnum0j×xj)    (i) (C_{num1}^{i} \times x^{i}) \times (C_{num0}^{j} \times x^{j}) ~~~~(i为奇数)

考虑将所有的式子相加后合并起来,那么右边这个可以写成 j=1num0xj\sum_{j=1}^{num0}{x^j},显然为(x+1)j(x+1)^{j}的展开式。

那么左边这个式子可以写成:
i=1,inum1xi \sum_{i=1,i为奇数}^{num1}{x^i}

根据套路,可以直接写成(1+x)i(1x)i2\frac{(1+x)^{i}-(1-x)^{i}}{2}

那么两部分就都可以快速幂计算O(logn)O(logn)出来,那么对于每个询问既可以O(log2n)O(log^2n)做了。

Code\mathcal{Code}

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月16日 星期三 08时21分01秒
*******************************/
#include<cstdio>
#include<algorithm>
#define int long long

using namespace std;

struct IO{
    template<typename T>
    IO & operator>>(T&res)
    {
        T q=1;char ch;
        while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
        res=(ch^48);
        while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
        res*=q;
        return *this;
    }
}cin;

const int maxn=1e5+10;
const int MAXN=1005;
const int inv=499122177;
const int LG=30;
const int mod=998244353;
int n,a[maxn],m,sum[maxn][LG+1],md,lim;

int ksm(long long a,int b)
{
    int res=1; a=(a+mod)%mod;
    while(b)
    {
        if(b&1)
            res=1ll*res*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return res;
}

void init()
{
    for(int i=LG;i>=0;i--)
        if(md>(1<<i))
        {
            lim=i+1;
            break;
        }
    for(int i=1;i<=n;i++)
    {
        for(int j=LG;j>=0;j--)
        {
            sum[i][j]=sum[i-1][j];
            if(a[i]&(1<<j))
                sum[i][j]++;
        }
    }
}

void solve(int l,int r,int x)
{
    long long ans=0;
    for(int k=0;k<=lim;k++)
    {
        int a=sum[r][k]-sum[l-1][k],b=r-l+1-a;
        ans=(ans+(1ll*ksm(1+x,a)-ksm(1-x,a)+mod)%mod*inv%mod*ksm(1+x,b)%mod*(1<<k)%mod)%mod;
    }
    printf("%lld\n",ans);
}

signed main()
{
    //freopen("score.in","r",stdin);
    //freopen("score.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i],md=max(a[i],md);
    init();
    int l,r,x;
    for(int i=1;i<=m;i++)
    {
        cin>>l>>r>>x;
        solve(l,r,x);
    }
    return 0;
}
相关标签: 二项式展开