题意
给定一个序列
a
1
,
2..
n
a_{1,2..n}
a1,2..n,定义
f
(
x
)
=
(
a
种
小
于
等
于
x
的
数
字
数
目
)
2
f(x)=(a种小于等于x的数字数目)^2
f(x)=(a种小于等于x的数字数目)2。
Q
Q
Q个询问
l
r
x
\ l\ r\ x
l r x,查询
∑
i
=
l
r
f
(
i
x
o
r
x
)
\sum_{i=l}^rf(i\ xor\ x)
∑i=lrf(i xor x)
数据范围:
1
≤
n
,
Q
≤
1
e
5
,
0
≤
l
≤
r
<
2
30
,
0
≤
x
,
a
i
<
2
30
1\le n,Q\le1e5,0\le l \le r < 2^{30},0\le x,a_i<2^{30}
1≤n,Q≤1e5,0≤l≤r<230,0≤x,ai<230
解题思路
区间的询问可以转换为对两个前缀
s
u
m
(
n
)
=
∑
i
=
0
n
f
(
i
x
o
r
x
)
sum(n)=\sum_{i=0}^nf(i\ xor\ x)
sum(n)=∑i=0nf(i xor x)的询问。答案是两个前缀相减:
s
u
m
(
r
)
=
s
u
m
(
l
−
1
)
sum(r)=sum(l-1)
sum(r)=sum(l−1)。
查询的i和一个乱七八糟的x异或了一下,导致询问的实际区间不是连续的,很麻烦。但是由异或的性质我们可以注意到,这些不连续的询问是由最多
l
o
g
(
x
)
log(x)
log(x)个连续的询问构成的:
比如查询的是
s
u
m
(
n
)
sum(n)
sum(n)
从高位到低位,比如进行到了
p
o
s
pos
pos位,如果n的这一位是1,那么i的这一位为0的时候,后面
(
i
x
o
r
x
)
(i\ xor\ x)
(i xor x)可以取的区间范围是
[
0
,
2
p
o
s
−
1
]
[0,2^{pos}-1]
[0,2pos−1]中的任意值。那么就得到了一个连续的区间。对这log个连续的区间进行查询就可以了。
对连续区间的答案查询有很多方法,这里我采用的是先利用前缀和处理出每一个
a
i
a_i
ai对应的
s
u
m
(
a
i
)
sum(a_i)
sum(ai),然后查询的时候二分一下就可以了。
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 1e5 + 50;
const ll mod = 998244353;
int a[maxn];
int cc[maxn], num = 0;
int n, q;
ll sum[maxn];
void init(){
cin>>n>>q;
for(int i = 0; i < n; ++i) scanf("%d", &a[i]), cc[++num] = a[i];
sort(a, a+n);
sort(cc+1,cc+num+1);
num = unique(cc+1,cc+num+1)-cc-1;
sum[0] = 0;
for(int i = 1; i < num; ++i){
ll k = upper_bound(a,a+n,cc[i])-a;
sum[i] = (sum[i-1]+(cc[i+1] - cc[i])%mod*k%mod*k%mod)%mod;
}
}
ll ask(int x){
int pos = upper_bound(cc+1,cc+1+num, x)-cc;
if(pos == 1) return 0;
pos--;
ll res = sum[pos-1];
ll k = upper_bound(a, a+n, cc[pos])-a;
res = (res + (x-cc[pos]+1)*k%mod*k%mod)%mod;
return res;
}
ll ask(int l, int r){
//cout<<"l:"<<l<<" r:"<<r<<endl;
return (ask(r) - ask(l-1))%mod;
}
ll qry(int t, int x){
if(t < 0) return 0;
int cur = 0;
ll res = 0;
for(int i = 30; i >= 0; --i){
int u = (x>>i&1);
if(t>>i&1){
res += ask(cur|(u<<i), (cur|(u<<i))+(1<<i)-1);
cur += ((1<<i)^(u<<i));
}else{
cur |= (u<<i);
}
}
res += ask(cur, cur);
return res;
}
void sol(){
while(q--){
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
ll ans = (qry(r, x) - qry(l-1, x))%mod;
ans = (ans + mod)%mod;
printf("%lld\n", ans);
}
}
int main()
{
init();
sol();
}