题意
给定一个序列
,定义
。
个询问
,查询
数据范围:
解题思路
区间的询问可以转换为对两个前缀
的询问。答案是两个前缀相减:
。
查询的i和一个乱七八糟的x异或了一下,导致询问的实际区间不是连续的,很麻烦。但是由异或的性质我们可以注意到,这些不连续的询问是由最多
个连续的询问构成的:
比如查询的是
从高位到低位,比如进行到了
位,如果n的这一位是1,那么i的这一位为0的时候,后面
可以取的区间范围是
中的任意值。那么就得到了一个连续的区间。对这log个连续的区间进行查询就可以了。
对连续区间的答案查询有很多方法,这里我采用的是先利用前缀和处理出每一个
对应的
,然后查询的时候二分一下就可以了。
#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();
}