LOJ502 题解

link

给定一棵有根树,每个点有颜色,每次加一个叶子,询问叶子到根的路径是不是所有颜色出现次数都是 的倍数,不是的话再问是不是只有一个颜色不是 的倍数,是的话输出这个颜色。

考虑如果是 的倍数,那么可以给每个颜色随机一个权值然后维护到根每个叶子的异或和,异或和为 代表出现次数都是偶数,否则可以进行一个希的哈,查询是否存在这个异或值对应的颜色。

然后考虑 的倍数,我们需要一个类似二进制异或的运算,连续三次与自己运算变为 。类比异或不难想到三进制不进位加法。然后这题就做完了,算法的大致流程是:

  • 对于每个颜色随机一个三进制值,并把这个值放入哈希表;
  • 每插入一个叶子,计算出它到根的三进制不进位加法值;
  • 如果为 输出则所有颜色都是 的倍数;
  • 否则查询哈希表,寻找是否有匹配的颜色。

这题卡空间,哈希需要链表实现差评,具体实现还参考了官方题解,哪里也有随机化概率的具体计算和证明。

还有一个小 TIP:正常的 rand() 只能生成 的随机数,有时候需要较大的随机数非常麻烦。从 c++11 开始提供了一个叫 mt19937 的随机数,比 rand() 快更随机还大。简单的用法:

1
2
3
4
5
6
7
8
9
mt19937 rd(time(0)); // 定义和播种
/*
mt19937 范围是 unsigned int
mt19937_64 更大,unsigned long long
*/
int foo = rd(); // 生成随机数
/*=============================*/
uniform_int_distribution<int>dist(1, n);
int foo = dist(rd); // 生成 [1,n] 的随机数

激动人心的代码时间~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long

inline int read() {
int x = 0, f = 0; char c = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = x * 10 + (c & 15), c = getchar();
return f ? -x : x;
}

#define N 1000005
#define M 2000005
int n, m;
ull hs[N], val[M];
mt19937_64 rd(time(0));

struct trinum {
int a[2188][2188];
trinum() { memset(a, 0, sizeof a);
for (int i = 0; i < 2187; i ++) {
for (int j = 0; j < 2187; j ++) {
int x = i, y = j, v = 1;
for (int k = 0; k < 7; k ++) {
int t = (x % 3 + y % 3) % 3;
a[i][j] += t * v, x /= 3, y /= 3, v *= 3;
}
}
}
}
ull ran() { return rd() % 12157665459056928801ull; }
ull tor(ull x, ull y) const {
ull r = 0, v = 1;
for (int i = 0; i < 6; i ++) {
r += a[x % 2187][y % 2187] * v;
x /= 2187, y /= 2187, v *= 2187;
}
return r;
}
} trin;

struct hash {
int a[M], nex[M], hed[M], tot, m; ull v[M];
hash() : m(1999993), tot(0) {
memset(hed, 0, sizeof hed);
memset(nex, 0, sizeof nex);
}
void add(ull x, int i) {
a[++ tot] = i;
v[tot] = x;
nex[tot] = hed[x % m];
hed[x % m] = tot;
}
int ask(ull x) {
for (int i = hed[x % m]; i; i = nex[i]) {
if (v[i] == x) return a[i];
}
return -2;
}
} hat;

int main() {
srand(time(NULL));
n = read(), m = read();
for (int i = 1; i <= n; i ++) {
hs[i] = trin.ran();
hat.add(hs[i], i);
hat.add(trin.tor(hs[i], hs[i]), i);
}
val[0] = 0;
for (int i = 1, la = 0; i <= m; i ++) {
int u = read() ^ la, fa = read() ^ la;
val[i] = trin.tor(hs[u], val[fa]);
la = val[i] ? hat.ask(val[i]) : -1;
printf("%d\n", la);
}
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!