[题解] 送分 二项式展开
程序员文章站
2022-06-06 22:20:35
...
题目描述
数据范围:
题解
看到这种异或统计答案的题可以立马想到二进制拆分的套路。
我们可以对每一个二进制位分别讨论:
设中所有元素第位为的个数有个,为的有个。
那么有贡献时当且仅当位为的被选了奇数次,为的被选任意次。
设第位为的有个被选,为的有个被选。
那么贡献为:
把这个式子变一下为:
考虑将所有的式子相加后合并起来,那么右边这个可以写成 ,显然为的展开式。
那么左边这个式子可以写成:
根据套路,可以直接写成。
那么两部分就都可以快速幂计算出来,那么对于每个询问既可以做了。
/*******************************
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;
}
上一篇: 算法之二项分布(c/c++版)
下一篇: tomcat源码研究之参数编码格式处理
推荐阅读