字典树小记

普通字典树

没什么好讲的

0-1 Trie

非常有用,经常用于异或相关的题目

求一个集合中两两异或的最大值

枚举集合中的一个数 \(x\),按位贪心,如果这一位有一个与 \(x\) 不同的,那么字典树上走这一边,否则走 \(x\) 的这一边。具体见代码

int solve(int x){
	int p = 1, o = 0, ans = 0;
	F_(i,30,0){
		int o = ((x>>i)&1);
		if (ch[p][!o]){
			p = ch[p][!o];
			ans += (1<<i); 
		} else {
			p = ch[p][o];
		}
	}
	return ans;
}

求两个集合中各选一个数异或的最小值

可以枚举第一个集合的数,也可以一次 \(dfs\)

int query(int u,int v,int bit){
	int res = 2e9;
	if (trie[u][0]&&trie[v][0]) res = min(res,query(trie[u][0],trie[v][0],bit-1));
	if (trie[u][1]&&trie[v][1]) res = min(res,query(trie[u][1],trie[v][1],bit-1));
	if (res < 2e9) return res;
	if (trie[u][0]&&trie[v][1]) res = min(res,(1<<bit)|query(trie[u][0],trie[v][1],bit-1));
	if (trie[u][1]&&trie[v][0]) res = min(res,(1<<bit)|query(trie[u][1],trie[v][0],bit-1));
	if (res==2e9) res = 0;
	return res;
}

可持久化 0-1 Trie

就是运用可持久化线段树的思想

P4735 最大异或和
求后缀不好处理,考虑用前缀和 \(s_n\) 异或一下 \(x\),转换为前缀和,直接上可持久化 0-1 Trie 即可,注意一些细节

#include <bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a; i<=b; ++i)
#define F_(i,a,b) for(int i=a; i>=b; i--)
const int N = 6e5+10;
int n,m,tot;
int ar[N],s[N];
int rt[N],cnt[N<<7],ch[N<<7][2];
void update(int p,int pre,int x){
	int u = ++tot;
	rt[p] = u;
	F_(i,25,0){
		int o = ((x>>i)&1);
		ch[u][o] = ++tot;
		ch[u][!o] = ch[pre][!o];
		pre = ch[pre][o];
		u = ch[u][o];
		cnt[u] = cnt[pre]+1; 
	}
}
int query(int u,int v,int x){
	int res = 0;
	F_(i,25,0){
		int o = ((x>>i)&1);
		//cout << cnt[ch[v][!o]]-cnt[ch[u][!o]] << "\n";
		if (cnt[ch[v][!o]] > cnt[ch[u][!o]]){
			u = ch[u][!o], v = ch[v][!o];
			res += (1<<i);
		} else {
			u = ch[u][o], v = ch[v][o];
		}
	}
	return res;
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
	cin >> n >> m;
	F(i,1,n){
		cin >> ar[i];
		s[i] = s[i-1]^ar[i];
		update(i,rt[i-1],s[i]);
	}
	while (m--){
		char op; int l,r,x;
		cin >> op;
		if (op == 'A'){
			cin >> x;
			++n;
			ar[n] = x; s[n] = s[n-1]^ar[n];
			update(n,rt[n-1],s[n]); 
		} else {
			cin >> l >> r >> x;
			x ^= s[n];
			l--, r--;
			if (l==0) cout << max(x,query(0,rt[r],x)) << "\n";
			else cout << query(rt[l-1],rt[r],x) << "\n";
		}
	}
	return 0;
}

P4098 [HEOI2013] ALO
枚举每个数作为次大值,找出两段极长合法区间,然后就是板子

posted @ 2025-11-13 21:08  huangyuze  阅读(3)  评论(0)    收藏  举报