题意: 给定一个长度为
n
n
n 的序列
c
,
q
c,q
c,q 次询问,每次给出
l
,
r
,
a
,
b
l,r,a,b
l,r,a,b求在
[
l
,
r
]
[l,r]
[l,r]中有多少种不同的值
k
k
k 满足
k
⊕
a
≤
b
?
.
k⊕a≤b?.
k⊕a≤b?.
题解: 莫队+字典树 补题的时候看到了带佬新得一种写法,即把字典树当成一颗完全二叉树去跑,不需要新建结点编号。
在 Trie 上顺着 a⊕b 走 ,为什么要跟着a⊕b走呢? 因为顺着a⊕b走也就是,顺着a⊕字典树=b走 当b=1时,就可以把c⊕a=0加入答案 若 b 当前位是 1,则所有 k⊕a 在当前位是 0 的数字都符合要求 找出字典树中与a当前为相同得值 (让其⊕后为0)
当然树状数组上套一个字典树也是一个不错得选择,先对r进行排序,然后离线询问,老套路题了。
代码:
#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct Q{
int l,r,k,a,b;
}pp[maxn];
int n,m,k;
int pos[maxn],a[maxn],cnt[maxn];
int sum[maxn*18],ans[maxn];
inline int read(){
int f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return x*f;
}
struct rule{
bool operator()(const Q & a,const Q & b)const{
if(pos[a.l]!=pos[b.l]) return a.l<b.l;
if(pos[a.l]&1) return a.r<b.r;
return a.r>b.r;
}
};
void add(int x,int v){
int node=1;
sum[node]+=v;
for(int i=17;i>=0;i--){
node=(node<<1)+(x>>i&1);
sum[node]+=v;
}
}
int query(int a,int b){
int node=1,res=0;
for(int i=17;i>=0;i--){
int t=(a>>i&1)^(b>>i&1);
if(b>>i&1) res+=sum[(node<<1)+(t^1)];
node=(node<<1)+t;
}
res+=sum[node];
return res;
}
int main() {
n=read();
int sz=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=read();
pos[i]=i/sz;
}
int q;
q=read();
for(int i=1;i<=q;i++){
pp[i].l=read();
pp[i].r=read();
pp[i].a=read();
pp[i].b=read();
pp[i].k=i;
}
sort(pp+1,pp+1+q,rule());
int l=1,r=0;
for(int i=1;i<=q;i++){
while(pp[i].l<l){
l--;
if(++cnt[a[l]]==1){
add(a[l],1);
}
}
while(pp[i].r>r){
r++;
if(++cnt[a[r]]==1){
add(a[r],1);
}
}
while(pp[i].l>l){
if(--cnt[a[l]]==0){
add(a[l],-1);
}
l++;
}
while(pp[i].r<r){
if(--cnt[a[r]]==0){
add(a[r],-1);
}
r--;
}
ans[pp[i].k]=query(pp[i].a,pp[i].b);
}
for(int i=1;i<=q;i++){
printf("%d\n",ans[i]);
}
}
|