【HDU-6291/2018CCPC女生赛E】对称数(散列+树上主席树)
题目链接:https://vjudge.net/problem/HDU-6291
思路
偶数个 \(x\) 异或起来一定是 \(0\)。因此只需要在链上找第一个出现次数为偶数次的点即可。
通过将一个数变为另一个极大的数,来满足异或后的和不会碰撞,例如,将 \(1\) 变为 \(114\),将 \(2\)变为 \(514\),将 \(3\) 变为 \(1919\),来保证 \(1 \bigoplus 2 \bigoplus 3 = 0\) 的情况不会出现导致干扰。
然后想到在树上搞颗权值线段树解决问题,如果做过SPOJ系列的求数上路径第 \(k\) 大的问题的话就能很快想到树上主席树。绕口令
花絮
我:这题可以线段树分裂合并乱搞。
juraws:那你快上
(半小时后)
我:不好意思样例没过好像复杂度也不太对劲……
另外,中途求 \(LCA\) 还挂了好几发,需要将初始的 \(fa\) 树组置0。
AC代码
#include <bits/stdc++.h>
typedef unsigned long long ull;
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXR = 2e5 + 1;
ull Newrnd() {
return ((ull) rand() << 45) | ((ull) rand() << 30) | (rand() << 15) | rand();
}
ull val2newval[MAXN], pre_val2newval[MAXN];
class HJT {
public:
int ch[MAXN * 70][2], tot = 0;
ull sum[MAXN * 70];
void init() {
memset(ch, 0, sizeof(ch));
memset(sum, 0, sizeof(sum));
tot = 0;
}
inline void push_up(int rt) {
sum[rt] = sum[ch[rt][0]] ^ sum[ch[rt][1]];
}
int update(int rt, int pos, ull val, int be, int en) {
int nrt = ++tot;
ch[nrt][0] = ch[nrt][1] = 0;
sum[nrt] = (ull) 0;// = rnd[nrt] = 0;
if (be == en) {
sum[nrt] = sum[rt] ^ val;
return nrt;
}
int mid = (be + en) >> 1;
if (pos <= mid) {
ch[nrt][0] = update(ch[rt][0], pos, val, be, mid);
ch[nrt][1] = ch[rt][1];
} else {
ch[nrt][0] = ch[rt][0];
ch[nrt][1] = update(ch[rt][1], pos, val, mid + 1, en);
}
push_up(nrt);
return nrt;
}
int query(int lrt, int rrt, int fa, int faa, int be, int en) {
if (be >= en) return be;
int mid = (be + en) >> 1;
ull delta1 = sum[ch[lrt][0]] ^sum[ch[rrt][0]] ^sum[ch[fa][0]] ^sum[ch[faa][0]];
ull delta2 = pre_val2newval[mid] ^pre_val2newval[be - 1];
if (delta1 != delta2) return query(ch[lrt][0], ch[rrt][0], ch[fa][0], ch[faa][0], be, mid);
else return query(ch[lrt][1], ch[rrt][1], ch[fa][1], ch[faa][1], mid + 1, en);
}
} tree;
struct Edge {
int to, nex;
} e[MAXN << 1];
int head[MAXN], tol;
void addEdge(int u, int v) {
e[tol].to = v, e[tol].nex = head[u], head[u] = tol, tol++;
}
int root[MAXN];
int dep[MAXN], fa[MAXN][32], lg[MAXN];
int a[MAXN];
void dfs(int u, int f) {
fa[u][0] = f;
dep[u] = dep[f] + 1;
root[u] = tree.update(root[f], a[u], val2newval[a[u]], 1, MAXR);
for (int i = 1; i <= lg[dep[u]]; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int i = head[u]; ~i; i = e[i].nex) {
int v = e[i].to;
if (v == f) continue;
dfs(v, u);
}
}
int LCA(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
while (dep[u] > dep[v]) u = fa[u][lg[dep[u] - dep[v]] - 1];
if (u == v) return u;
for (int k = lg[dep[u]] - 1; k >= 0; k--) {
if (fa[u][k] != fa[v][k]) u = fa[u][k], v = fa[v][k];
}
return fa[u][0];
}
int main() {
srand(114514); // random
for (int i = 1; i < MAXN; i++) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
pre_val2newval[0] = 0;
for (int i = 1; i < MAXN; i++) val2newval[i] = Newrnd(), pre_val2newval[i] = pre_val2newval[i - 1] ^ val2newval[i];
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) head[i] = -1; // init graph
memset(fa, 0, sizeof(fa));
tol = 0; // init graph
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 2; i <= n; i++) {
int u, v;
scanf("%d%d", &u, &v);
addEdge(u, v), addEdge(v, u);
}
tree.tot = 0; // init HJT
root[0] = 0;
dfs(1, 0);
while (m--) {
int u, v;
scanf("%d%d", &u, &v);
int lca = LCA(u, v);
// if (dep[u] > dep[v]) swap(u, v);
printf("%d\n", tree.query(root[u], root[v], root[lca], root[fa[lca][0]], 1, MAXR));
}
}
}