题目链接: Set
大致题意
给出n个集合, 初始集合i内有数字 a i a_i ai.
有m个操作:
1 x y
如果
a
x
a_x
ax和
a
y
a_y
ay不在同一个集合, 则把两个数字所在集合合并.
2 x
给
a
x
a_x
ax所在的集合中的所有数字加一
3 x k c
询问
a
x
a_x
ax所在集合中的所有数字在%
2
k
2^k
2k的情况下有多少个和
c
c
c相同.
解题思路
01Trie
我们先考虑查询操作, 我们如何查询若干个数字的后k位数否和一个数字相同呢?
我们发现可以用01Trie来维护. 每个节点上维护数字个数即可.
考虑到操作②, 如果我们给Trie树上的数字进行+1操作:
对于最低位为0的数字(偶数), 加1后最低位变成了1.
对于最低位为1的数字(奇数), 加1后最低位变成了0, 并且次低位进位1.
我们对于次低位进位的数字继续分析, 发现与次低位的情况相同.
因此我们得出结论: 在Trie树中进行+1操作, 每次需要交换Trie树的左右儿子, 对于交换后为0的节点, 需要递归继续进行交换操作. (复杂度: log值域).
对于操作①, 我们首先需要维护两个数字是否在同一个集合. 我们很容易想到用并查集去维护.
如果两个数字不在同一个集合, 则我们需要去合并两棵Trie树.
考虑到Trie树的本质就是动态开点的权值线段树, 因此我们可以用线段树合并的方法来合并Trie.
代码细节:
本题的Trie需要从低位到高位存储数字, 与通常我们写的Trie不同.
所有的操作都是对于数字所在集合进行操作的, 因此不要忘记find()一下.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 6E5 + 10, B = 31, M = N * B;
int p[N];
int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); }
int t[M][2], ind;
int root[N], cou[M];
void insert(int a, int c) {
int x = root[a]; cou[x]++;
for (int i = 0; i <= B - 1; ++i) {
int w = c >> i & 1;
if (!t[x][w]) t[x][w] = ++ind;
x = t[x][w]; cou[x]++;
}
}
int merge(int x, int y) {
if (!x or !y) return x | y;
cou[x] += cou[y];
t[x][0] = merge(t[x][0], t[y][0]);
t[x][1] = merge(t[x][1], t[y][1]);
return x;
}
void unite(int a, int b) {
a = find(a), b = find(b);
if (a == b) return;
root[a] = merge(root[a], root[b]);
p[b] = a;
}
void modify(int x) {
if (!x) return;
swap(t[x][0], t[x][1]);
modify(t[x][0]);
}
int ask(int a, int k, int c) {
int x = root[a];
for (int i = 0; i < k; ++i) {
int w = c >> i & 1;
if (!t[x][w]) return 0;
x = t[x][w];
}
return cou[x];
}
int main()
{
int n, m; cin >> n >> m;
rep(i, n) {
p[i] = i;
int x; scanf("%d", &x);
root[i] = ++ind, insert(i, x);
}
rep(i, m) {
int tp; scanf("%d", &tp);
if (tp == 1) {
int a, b; scanf("%d %d", &a, &b);
unite(a, b);
}
else if (tp == 2) {
int x; scanf("%d", &x);
x = find(x);
modify(root[x]);
}
else {
int a, k, c; scanf("%d %d %d", &a, &k, &c);
a = find(a);
printf("%d\n", ask(a, k, c));
}
}
return 0;
}