D e s c r i p t i o n \mathcal{Description} Description
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}
2n1−1中方案使这一位有奇数个
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}
bit∑2bitlen∑xleni,i&1=1∑(n1i)(n0len−i)
另一个想法是分治,先解决出左边长度为
i
i
i的方案数和解决右边长度为
n
−
i
n-i
n−i的方案数
则长度为
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/1−i,会发现这就是
(
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)xi1n1−i⎠⎞(i∑(n0i)xi1n0−i)
考虑算出左边可以为偶数的值,再减去为偶数的项,实际上
(
x
+
1
)
n
−
(
1
−
x
)
n
2
\frac{\left(x+1\right)^n-\left(1-x\right)^n}{2}
2(x+1)n−(1−x)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−(1−x)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;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧