01-Trie 学习

其实就是把二进制数放在 Trie 上面,然后就可以解决很多问题

最大 xor 数对

就是把所有的数插到 Trie 上,每次查询每一位上有不同就走不同的边

#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 3e5 + 10;
int n,tot,ans,ch[maxn*31][2];
inline void insert(int x) {
	int u = 0;
	for (int i = 31;~i;i--) {
		int bit = (x>>i)&1;
		if (!ch[u][bit]) ch[u][bit] = ++tot;
		u = ch[u][bit];
	}
}
inline int query(int x) {
	int u = 0,ans = 0;
	for (int i = 31;~i;i--) {
		int bit = (x>>i)&1;
		if (ch[u][bit^1]) { u = ch[u][bit^1]; ans |= 1<<i; }
		else u = ch[u][bit];
	}
	return ans;
}
int main() {
	scanf("%d",&n);
	for (int i = 1,x;i <= n;i++) { scanf("%d",&x); insert(x); ans = max(ans,query(x)); }
	printf("%d",ans);
	return 0;
}

树上最大 xor 路径

记一个 \(d_i\)\(i\) 到根的 xor

就变成最大的 \(\{d\}\) 点对了

CF923C Perfect Security

题意:给两个序列 \(\{a\}\)\(\{b\}\),求字典序最小的 \(\{c\}\) 使得 \(a_i\ xor\ c_i = b_i\)

字典序最小要求 \(c_i = a_i\ xor\ b_i\) 尽量小,所以按顺序找 \(a_i\ xor\ b_i\) 最小,然后删除 \(b_i\) 即可

#include <cstdio>
const int maxn = 3e5 + 10;
int n,tot(1),a[maxn],ch[maxn*30][2],siz[maxn*30];
inline void insert(int u,int i,int x) {
	if (i < 0) return void(siz[u]++);
	int bit = (x>>i)&1;
	if (!ch[u][bit]) ch[u][bit] = ++tot;
	insert(ch[u][bit],i-1,x);
	siz[u] = siz[ch[u][0]]+siz[ch[u][1]];
}
inline int query(int x) {
	int u = 1,ans = 0;
	for (int i = 30;~i;i--) {
		int bit = (x>>i)&1; siz[u]--;
		if (ch[u][bit] && siz[ch[u][bit]]) u = ch[u][bit];
		else { u = ch[u][bit^1]; ans |= 1<<i; }
	}
	siz[u]--; //!!!
	return ans;
}
int main() {
	scanf("%d",&n);
	for (int i = 1;i <= n;i++) scanf("%d",&a[i]);
	for (int i = 1,x;i <= n;i++) { scanf("%d",&x); insert(1,30,x); }
	for (int i = 1;i <= n;i++) printf("%d ",query(a[i]));
	return 0;
}

CF842D Vitya and Strange Lesson

题意:给一个数列,每次操作将所有数 \(xor\ x\),求操作后的 Mex

在 Trie 树上,若 0 边连的子树不是满的,Mex 就在 0 边连的子树中,否则在 1 边连的子树中

再是 xor,发现 \(x\) 的某一位上是 1,就会交换 Trie 树的左右子树,但是不要真的交换,找 Mex 的时候走相反的边就好了

注意这题的 siz 不再是子树内有多少个数,而是有多少个叶子节点有数(即相同的数只算一遍)


#include <cstdio>
const int maxn = 3e5 + 10;
int n,m,tot(1),ch[maxn*21][2],siz[maxn*21];
inline void insert(int u,int i,int x) {
	if (i < 0) return void(siz[u] = 1); //!!! =1
	int bit = (x>>i)&1;
	if (!ch[u][bit]) ch[u][bit] = ++tot;
	insert(ch[u][bit],i-1,x);
	siz[u] = siz[ch[u][0]]+siz[ch[u][1]];
}
inline int query(int x) {
	int u = 1,ans = 0;
	for (int i = 20;~i;i--) {
		int bit = (x>>i)&1;
		if (siz[ch[u][bit]] == (1<<i)) {
			u = ch[u][bit^1];
			ans |= 1<<i;
		} else u = ch[u][bit];
		if (!u) return ans;
	}
	return ans;
}
int main() {
	scanf("%d%d",&n,&m);
	for (int x;n--;insert(1,20,x)) scanf("%d",&x);
	for (int x = 0,y;m--;printf("%d\n",query(x ^= y))) scanf("%d",&y);
	return 0;
}

CF1416C XOR Inverse

题意:给一个数列 \(\{a\}\),求一个最小的 \(x\) 使得 \(a_i = a_i\ xor\ x\)\(\{a\}\) 的逆序对最少

和上题一样,若 \(x\) 的某一位上是 1,就相当于交换了那一位那层的 Trie 的所有左右子树

再考虑哪些位上要交换,可以发现 Trie 树叶子上,一个点在另一个点左边,那右边这个点一定小于左边的点

那可以将每一个数依次插入 Trie 上,Trie 上的结点这个子树叶子的结点的编号

可以发现叶子节点编号的逆序对就是原数组的逆序对,因为原数组若一个数后面有比它小的数,小的数会比它后加入并且插入 Trie 的叶子后在它的前面

因为两颗子树内部数的顺序是不影响两棵之间的逆序对个数的,所以就可以计算每一层所有子树反转或不反转的逆序对个数了

#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 3e5 + 10;
const int N = 30;
vector<int> num[maxn*N];
int n,tot,ch[maxn*N][2];
long long cnt[N+1][2];
inline void insert(int x,int v) {
	int u = 0;
	for (int i = N;~i;i--) {
		int bit = (x>>i)&1;
		num[u].push_back(v);
		if (!ch[u][bit]) ch[u][bit] = ++tot;
		u = ch[u][bit];
	}
	num[u].push_back(v);
}
inline void solve(int u,int i) {
	if (i < 0) return;
	if (ch[u][0]) solve(ch[u][0],i-1);
	if (ch[u][1]) solve(ch[u][1],i-1);
	if (!ch[u][0] || !ch[u][1]) return;
	long long res = 0;
	for (size_t j = 0;j < num[ch[u][1]].size();j++)
		res += num[ch[u][0]].end()-lower_bound(num[ch[u][0]].begin(),num[ch[u][0]].end(),num[ch[u][1]][j]);
	cnt[i][0] += res;
	cnt[i][1] += 1ll*num[ch[u][0]].size()*num[ch[u][1]].size()-res;
}
int main() {
	scanf("%d",&n);
	for (int i = 1,x;i <= n;i++) { scanf("%d",&x); insert(x,i); }
	solve(0,N);
	long long ans = 0;
	int res = 0;
	for (int i = 0;i <= N;i++)
		if (cnt[i][0] <= cnt[i][1]) ans += cnt[i][0];
		else { ans += cnt[i][1]; res |= 1<<i; }
	printf("%lld %d",ans,res);
	return 0;
}
posted @ 2020-11-25 22:40  lrj124  阅读(120)  评论(0编辑  收藏  举报