专栏文章

题解:P11209 『STA - R8』小熊游景点 II

P11209题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miomv1sn
此快照首次捕获于
2025/12/02 21:48
3 个月前
此快照最后确认于
2025/12/02 21:48
3 个月前
查看原文
这道题目能想到正解还是不容易的。
首先看数据范围:1n,m5×1051 \le n,m \le 5 \times 10^5。如果暴力做肯定超时。

前置知识

字典树,模板题传送门

具体过程

那么看到题目中的异或运算,我们可以试着往字典树的方向来思考题目。
建一个只有 0011 的字典树,对于每个结点都有一个标记 xx,表示从根结点到这个结点所形成的二进制数 kk 能满足 xxii 使得 (aik)bi(a_i\oplus k)\le b_i,其中 \oplus 是按位异或。
完成定义后,就开始来思考,进行分类讨论:
  1. 如果在二进制下这一位的 bib_i11,这个时候异或的结果可以有两种情况,如果异或的结果为 00,则一定满足条件,kk 的这一位就要等于 aia_i 的这一位,这时就不用往下走了,直接给那个结点的标记 +1+ 1。如果异或的结果为 11,则还需要考虑后面的位数,于是就继续往下走。需要注意的是,如果到了最后一位,异或的结果为 11 时也能满足条件,也要给更新标记。
  2. 如果在二进制下这一位的 bib_i00,这个时候异或的结果只能有一种情况,那就是 00,这个时候 kk 的这一位等于 aia_i 的这一位。然后还需要继续往下走,同样要特判最后一位。
这样就基本解决完了。在计算答案时,把 kk 的二进制在字典树上走一遍,把一路上的标记都相加即可。

代码

理解透了代码就很好写。
CPP
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5, MX = 15000005;
int n, T, trie[MX][3], a[N], b[N], Q, lst, jd[MX], cnt;
void ins(int num, int num2){
	int now = 0;
	for(int i = 29;i >= 0;i--){
		int zh = ((num >> i) & 1), zh2 = ((num2 >> i) & 1);
		if(zh2){
			//如果没有这个结点就新建一个结点
			if(trie[now][zh] == 0) trie[now][zh] = ++cnt; 
			jd[trie[now][zh]]++;
			if(trie[now][zh ^ 1] == 0) trie[now][zh ^ 1] = ++cnt;
			now = trie[now][zh ^ 1];
			if(i == 0) jd[now]++;//特判i==0 
		} else{
			if(trie[now][zh] == 0) trie[now][zh] = ++cnt;
			now = trie[now][zh];
			if(i == 0) jd[now]++;
		}
	}
}
int fin(int num){
	int now = 0, ans = 0;
	for(int i = 29;i >= 0;i--){
		int zh = ((num >> i) & 1);
		if(trie[now][zh] == 0) break;
		now = trie[now][zh];
		//把k的二进制在字典树上走一遍,把所有标记相加
		ans += jd[now];
	}
	return ans;
}
int main(){
    scanf("%d%d%d", &T, &n, &Q);
    for(int i = 1;i <= n;i++) scanf("%d", &a[i]);
    for(int i = 1;i <= n;i++){
    	scanf("%d", &b[i]);
    	ins(a[i], b[i]);
	}
    while(Q--){
    	int k;
    	scanf("%d", &k);
    	k = k ^ (lst * T);
    	int da = fin(k);
    	printf("%d\n", da);
    	lst = da;
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...