P7502 「HMOI R1」不知道是啥的垃圾题

一道很有启发性的题目。

考虑到由于是自然数对的比较,所以我们不能直接用 \(01\text{Trie}\)

但是考虑到我们可以将 \(\text{Trie}\) 转换成有 \(4\) 个出边的,每一个出边表示数对当前位的 \(01\) 情况。

然后你考虑查询,对于当前位置,存在一种大于的情况,两种等于的情况,一种小于的情况。

考虑到大于的情况我们可以直接计数,等于的情况就需要递归了,但是由于我们每一层都需要递归两次,效率过低,这该怎么办?

考虑到我们大于情况是只需要数量的,我们可以考虑不建儿子,这样的话对于每一个点,我们可以将 \(00\)\(11\) 合并,\(01\)\(10\) 合并。这样无论是怎样的情况,等于的递归就只需要进入一个节点即可。

然后每一个节点只需要储存一个数在当前位是 \(0\)\(1\) 的个数就行了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,K=63;
int n;
struct Trie{
	int rt,tot;Trie(){rt=tot=1;}
	struct Node{int son[2],size[2];}tr[N*K];
	void insert(long long x,long long y,int z){
		int u=rt;
		for(int i=K;i>=0;--i){
			int tx=((x>>i)&1),ty=((y>>i)&1);
			if(!tr[u].son[tx^ty]) tr[u].son[tx^ty]=++tot;
			u=tr[u].son[tx^ty],tr[u].size[tx]+=z;
		}
	}
	int query(long long x,long long y){
		int u=rt,res=0;
		for(int i=K;i>=0;--i){
			int tx=((x>>i)&1),ty=((y>>i)&1);
			res+=tr[tr[u].son[tx^ty^1]].size[tx^1];
			u=tr[u].son[tx^ty];
		}
		return res;
	}
}t;
signed main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		int opt;long long x,y;
		scanf("%d%lld%lld",&opt,&x,&y);
		if(opt==1) t.insert(x,y,1);
		else if(opt==2) t.insert(x,y,-1);
		else printf("%d\n",t.query(x,y));
	}
	return 0;
}
posted @ 2021-09-26 16:40  Point_King  阅读(71)  评论(0编辑  收藏  举报