n
n
n座岛屿编号为
1
−
n
1-n
1−n,每个岛屿有两个值
a
i
,
b
i
a_i,b_i
ai,bi。
q
q
q次询问,给出
l
,
r
,
c
,
d
l,r,c,d
l,r,c,d,求编号在
[
l
,
r
]
[l,r]
[l,r]中的岛屿满足
a
⨁
c
≤
min
(
b
,
d
)
aigoplus cle min(b,d)
a⨁c≤min(b,d)的数量。
n
,
q
≤
1
0
5
,
1
≤
a
,
b
,
c
,
d
≤
2
24
−
1
n,qle10^5,1le a,b,c,dle2^{24}-1
n,q≤105,1≤a,b,c,d≤224−1
题解
部分分给了很清晰的提示。
情况一:始终满足
d
≤
b
dle b
d≤b,则每个岛的
b
b
b并没有用,把
a
a
a插入
0
/
1
0/1
0/1的Trie树中,再把询问
c
,
d
c,d
c,d带进去查询。这里需要把询问拆成
l
−
1
l-1
l−1和
r
r
r,然后和编号一起排序离线查询。查询具体的做法是,若
d
d
d当前位为
0
0
0,则进入异或后为
0
0
0的子树;否则加上异或后为
0
0
0的子树,再进入异或后为
1
1
1的子树。
情况二:始终满足
b
≤
d
ble d
b≤d,则每个询问的
d
d
d没有用,把
a
a
a和
b
b
b一起插入Trie树中,插入的方法和情况一查询的方法类似,把满足条件的子树打上标记。然后再把
c
c
c带进去查询。同样需要把询问拆成两部分。
满分的做法,可以用CDQ分治,先把询问和插入按
d
d
d和
b
b
b一起排序,然后分成上面两种情况做,每个区间都按编号再排一次序,两个指针边扫边插入和查询即可。
要注意的是查询到Trie树的叶子节点也要加入答案,插入到Trie树的叶子节点也要打上标记。
代码
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define N 100010struct node {int o, x, k, c, r;}a[N *3], p[N *3], q[N *3], st[N *3];struct{int p[2], s;}f[N *48];int ans[N], sum =1;intcmp(node x, node y){return x.x < y.x;}voidinsert(int k,int c){int x =1;for(int i =23; i >=0; i--){int o =0;if(k &(1<< i)) o =1;if(!f[x].p[o]) f[x].p[o]=++sum;
x = f[x].p[o];
f[x].s += c;}}intfind(int k,int mi){int s =0, x =1;for(int i =23; i >=0; i--){int o =0;if(k &(1<< i)) o =1;if(mi &(1<< i)){if(f[x].p[o]) s += f[f[x].p[o]].s;if(f[x].p[!o]) x = f[x].p[!o];elsebreak;}else{if(f[x].p[o]) x = f[x].p[o];elsebreak;}if(i ==0) s += f[x].s;}return s;}voidchange(int k,int mi,int c){int x =1;for(int i =23; i >=0; i--){int o =0;if(k &(1<< i)) o =1;if(mi &(1<< i)){if(!f[x].p[o]) f[x].p[o]=++sum;
f[f[x].p[o]].s += c;if(!f[x].p[!o]) f[x].p[!o]=++sum;
x = f[x].p[!o];}else{if(!f[x].p[o]) f[x].p[o]=++sum;
x = f[x].p[o];}}
f[x].s += c;}intget(int k){int x =1, s =0;for(int i =23; i >=0; i--){int o =0;if(k &(1<< i)) o =1;if(f[x].p[o]) x = f[x].p[o];elsebreak;
s += f[x].s;}return s;}voidsolve(int l,int r){if(l == r)return;int mid =(l + r)/2;solve(l, mid);solve(mid +1, r);int ps =0, qs =0, i;for(i = l; i <= mid; i++)if(a[i].o) p[++ps]= a[i];for(i = mid +1; i <= r; i++)if(!a[i].o) q[++qs]= a[i];int x =1;for(i =1; i <= ps; i++){while(x <= qs && q[x].r <= p[i].r)insert(q[x].k,1), x++;
ans[p[i].o]+=find(p[i].k, p[i].x)* p[i].c;}for(i =1; i <= qs && q[i].r <= p[ps].r; i++)insert(q[i].k,-1);
ps = qs =0;for(i = l; i <= mid; i++)if(!a[i].o) p[++ps]= a[i];for(i = mid +1; i <= r; i++)if(a[i].o) q[++qs]= a[i];
x =1;for(i =1; i <= qs; i++){while(x <= ps && p[x].r <= q[i].r)change(p[x].k, p[x].x,1), x++;
ans[q[i].o]+=get(q[i].k)* q[i].c;}for(i =1; i <= ps && p[i].r <= q[qs].r; i++)change(p[i].k, p[i].x,-1);int j = l, k = mid +1, t0 = l -1;while(j <= mid || k <= r){if(j <= mid &&(k > r || a[j].r <= a[k].r)) st[++t0]= a[j], j++;else st[++t0]= a[k], k++;}for(i = l; i <= r; i++) a[i]= st[i];}intmain(){int n, Q, i, j, tot =0;scanf("%d%d",&n,&Q);for(i =1; i <= n; i++){scanf("%d%d",&a[i].k,&a[i].x);
a[++tot].o =0, a[tot].r = i;}for(i =1; i <= Q; i++){int L, R, C, D;scanf("%d%d%d%d",&L,&R,&C,&D);
a[++tot].o = i, a[tot].r = R, a[tot].k = C, a[tot].x = D, a[tot].c =1;
a[++tot].o = i, a[tot].r = L -1, a[tot].k = C, a[tot].x = D, a[tot].c =-1;}sort(a +1, a + tot +1, cmp);solve(1, tot);for(i =1; i <= Q; i++)printf("%d
", ans[i]);return0;}