送分

也许更好的阅读体验

D e s c r i p t i o n \mathcal{Description} Description

pro785f8.pngdata08e3c.png
S o l u t i o n \mathcal{Solution} Solution
有位运算先按位考虑贡献
考虑若区间长度没有特殊贡献,即所有长度的贡献是一样的
那么答案就是这一位异或起来为 1 1 1的子集个数
关于这个,只需知道在这个区间中在这一位为 1 1 1的数量 n 1 n_1 n1和为 0 0 0的数量 n 0 n_0 n0即可
异或起来为 1 1 1就要求有奇数个 1 1 1和任意个 0 0 0
选这一位有 1 1 1的方案数总数为 2 n 1 2^{n_1} 2n1,其中一半有奇数个 1 1 1,一半有偶数个 1 1 1,所以有 2 n 1 − 1 2^{n_1-1} 2n11中方案使这一位有奇数个 1 1 1
所以总方案就是 2 n 1 × 2 n 0 2^{n_1}\times 2^{n_0} 2n1×2n0

可惜区间长度是有贡献的,所以上面方法是不行的
容易想到的是按位考虑后再按长度考虑,这样的复杂度是 O ( n 3 l o g n ) O(n^3logn) O(n3logn),结合上面可以得到 50 50 50分的部分分
∑ b i t 2 b i t ∑ l e n x l e n ∑ i , i & 1 = 1 ( n 1 i ) ( n 0 l e n − i ) \begin{aligned}\sum_{bit}2^{bit}\sum_{len}x^{len}\sum_{i,i\&1=1}\begin{pmatrix}n_1\\ i\end{pmatrix}\begin{pmatrix}n_0\\ len-i\end{pmatrix}\end{aligned} bit2bitlenxleni,i&1=1(n1i)(n0leni)

另一个想法是分治,先解决出左边长度为 i i i的方案数和解决右边长度为 n − i n-i ni的方案数
则长度为 n n n的可以由左右两边的方案结合得到,容易发现这是个卷积的形式,可以用 F F T FFT FFT优化

到此,上面的方法都不是正解

上面的方法都是单独考虑区间长度进行计算的,所以复杂度一定带了个 n n n,考虑一次性算出所有的答案
于是考虑将 x i x^i xi和组合数套起来,得到 a n s = ( ∑ i , i & 1 = 1 ( n 1 i ) x i ) ( ∑ i ( n 0 i ) x i ) \begin{aligned}ans=\left(\sum_{i,i\&1=1}\begin{pmatrix}n_1\\i\end{pmatrix}x^i\right)\left(\sum_i\begin{pmatrix}n_0\\i\end{pmatrix}x^i\right)\end{aligned} ans=i,i&1=1(n1i)xi(i(n0i)xi)

那个 i & 1 = 1 i\&1=1 i&1=1亦可写作 i % 2 = = 1 i\%2==1 i%2==1,这个可以用单位根反演做,但其实没有必要

仔细看一下这个式子, x i x^i xi旁乘一个 1 n 0 / 1 − i 1^{n_{0/1}-i} 1n0/1i,会发现这就是 ( x + 1 ) n 0 / 1 \left(x+1\right)^{n_{0/1}} (x+1)n0/1二项式展开后的结果
a n s = ( ∑ i , i & 1 = 1 ( n 1 i ) x i 1 n 1 − i ) ( ∑ i ( n 0 i ) x i 1 n 0 − i ) \begin{aligned}ans=\left(\sum_{i,i\&1=1}\begin{pmatrix}n_1\\i\end{pmatrix}x^i1^{n_1-i}\right)\left(\sum_i\begin{pmatrix}n_0\\i\end{pmatrix}x^i1^{n_0-i}\right)\end{aligned} ans=i,i&1=1(n1i)xi1n1i(i(n0i)xi1n0i)
考虑算出左边可以为偶数的值,再减去为偶数的项,实际上 ( x + 1 ) n − ( 1 − x ) n 2 \frac{\left(x+1\right)^n-\left(1-x\right)^n}{2} 2(x+1)n(1x)n就是左边 i i i为奇数的答案
最后得到
a n s = ( ( x + 1 ) n 1 − ( 1 − x ) n 1 2 ) ( x + 1 ) n 0 \begin{aligned}ans=\left(\frac{\left(x+1\right)^{n_1}-\left(1-x\right)^{n_1}}{2}\right)\left(x+1\right)^{n_0}\end{aligned} ans=(2(x+1)n1(1x)n1)(x+1)n0

这样一次询问每一位就是 l o g n logn logn,总复杂度就是 n l o g 2 n nlog^2n nlog2n

C o d e \mathcal{Code} Code

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月16日 星期三 08时59分51秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int limit = 35;
const int maxn = 100005;
const int mod = 998244353;
const int inv = 499122177;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	res=~res+1;
		return *this;
	}
}cin;
//}}}
int n,m,lim;
int a[maxn];
int s[limit][maxn];
//{{{ksm
int ksm (int a,int b)
{
	a%=mod;
	int s=1;
	for (;b;b>>=1,a=1ll*a*a%mod)
		if (b&1)	s=1ll*s*a%mod;
	return s;
}
//}}}
int main()
{
	cin>>n>>m;
	for (int i=1;i<=n;++i)	cin>>a[i],lim=max(lim,a[i]);
	for (int i=30;~i;--i)
		if (lim>(1<<i)){	lim=i+1;break;}

	for (int i=0;i<=lim;++i)
		for (int j=1;j<=n;++j)	s[i][j]=s[i][j-1]+((a[j]>>i)&1);

	while (m--){
		int l,r,x,ans=0;
		cin>>l>>r>>x;
		int le=r-l+1;
		for (int b=0;b<=lim;++b){
			int n1=s[b][r]-s[b][l-1],n0=le-n1,mi=(1<<b)%mod;
			ans=(ans+1ll*(ksm(x+1,n1)-ksm(mod+1-x,n1)+mod)%mod*inv%mod*ksm(x+1,n0)%mod*mi%mod)%mod;
		}
		printf("%d\n",ans);
	}
	return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值