2019下学期第二次个人周赛—A题

题意:

如题所示,求 \(S(u_1,v_1)\) \(\oplus\) \(S(u_2,v_2)\) 的最大值。

分析:

\(1\).暴力解法:既然 \(S(u,v)\) 与每个点的祖先有关,那么不难想到一个 \(O(n^2)\) 的方法计算所有 \(S(u,v)\) 的值,对每个顶点遍历其祖先暴力计算即可。要算 \(S(u_1,v_1)\) \(\oplus\) \(S(u_2,v_2)\) 的最大值,可以暴力 \(O(p^{2})\) 的计算,其中 \(p\) 表示去重后所有 \(S(u,v)\) 的数目。那么总复杂度就是 \(O(p^2)\) 的,无法通过本题。\(p\) 的最大值不超过 \(20000*15\)

\(2\).我的解法:可以发现每个顶点的权值很小,只需要用一个数组 \(vis[N][16]\) 来标记 \(u\) 的祖先到 \(u\) 这个节点的异或值,当 \(dfs\) 遍历 \(u\) 的儿子 \(v\) 时,再计算标记 \(v\) 的异或值即可。这样即可 \(O(n*16)\) 的得到所有 \(S(u,v)\) 的值。再用一棵 \(01\) 字典树边插入边查询最大值即可,总复杂度 \(O(p*log_2(p))\)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

#define pb push_back

const int N = 2e4 + 5, M = N * 16 + 5;

int n, u, v, ans, w[N];
bool val[N][17], flag[M];
int tot, ch[M * 19][2];
int head[N], cnt;

struct Graph {
	int v, next;
} edge[N << 1];

void addedge(int u, int v) {
	edge[++cnt].v = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

void insert(int p) {
	int u = 0, x;
	for (int i = 19; ~i; i--) {
		x = (p >> i) & 1;
		if (!ch[u][x]) ch[u][x] = ++tot;
		u = ch[u][x];
	}
}

int query(int p) {
	int u = 0, x, ans = 0;
	for (int i = 19; ~i; i--) {
		x = (p >> i) & 1;
		if (ch[u][!x]) u = ch[u][!x], ans |= (1 << i);
		else u = ch[u][x];
	}
	return ans;
}

void dfs(int u, int f) {
	val[u][w[u]] = true;
	if (!flag[w[u] * u]) {
		flag[w[u] * u] = true;
		insert(w[u] * u);
		ans = max(ans, query(w[u] * u));
	}
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].v;
		if (v == f) continue;
		for (int j = 0; j <= 15; j++) {
			if (val[u][j]) {
				val[v][j ^ w[v]] = true;
				if (!flag[(j ^ w[v]) * v]) {
					flag[(j ^ w[v]) * v] = true;
					insert((j ^ w[v]) * v);
					ans = max(ans, query((j ^ w[v]) * v));
				}
			}
		}
		dfs(v, u);
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n - 1; i++) {
		scanf("%d %d", &u, &v);
		addedge(u, v), addedge(v, u);
	}
	for (int i = 1; i <= n; i++) scanf("%d", w + i);
	dfs(1, 0);
	printf("%d\n", ans);
	return 0;
}
posted @ 2019-10-26 19:33  Chase。  阅读(125)  评论(0编辑  收藏  举报