1. 程式人生 > >[Luogu P3292] [BZOJ 4568] [SCOI2016]幸運數字

[Luogu P3292] [BZOJ 4568] [SCOI2016]幸運數字

洛谷傳送門

題目描述

A 國共有 nn 座城市,這些城市由 n1n-1 條道路相連,使得任意兩座城市可以互達,且路徑唯一。每座城市都有一個幸運數字,以紀念碑的形式矗立在這座城市的正中心,作為城市的象徵。

一些旅行者希望遊覽 A 國。旅行者計劃乘飛機降落在 xx 號城市,沿著 xx 號城市到 yy 號城市之間那條唯一的路徑遊覽,最終從 yy 城市起飛離開 A 國。在經過每一座城市時,遊覽者就會有機會與這座城市的幸運數字拍照,從而將這份幸運儲存到自己身上。然而,幸運是不能簡單疊加的,這一點遊覽者也十分清楚。他們迷信著幸運數字是以異或的方式保留在自己身上的。

例如,遊覽者拍了 3

3 張照片,幸運值分別是 55771111,那麼最終保留在自己身上的幸運值就是 995xor7xor115\ xor\ 7\ xor\ 11)。

有些聰明的遊覽者發現,只要選擇性地進行拍照,便能獲得更大的幸運值。例如在上述三個幸運值中,只選擇 551111 ,可以保留的幸運值為 1414 。現在,一些遊覽者找到了聰明的你,希望你幫他們計算出在他們的行程安排中可以保留的最大幸運值是多少。

輸入輸出格式

輸入格式:

第一行包含 22 個正整數 nnqq,分別表示城市的數量和旅行者數量。

第二行包含 nn 個非負整數,其中第 ii 個整數 G

iG_i 表示 ii 號城市的幸運值。

隨後 n1n-1 行,每行包含兩個正整數 xxyy,表示 xx 號城市和 yy 號城市之間有一條道路相連。

隨後 qq 行,每行包含兩個正整數 xxyy,表示這名旅行者的旅行計劃是從 xx 號城市到 yy 號城市。N20000,Q200000,Gi260N\leq 20000,Q\leq 200000,G_i\leq 2^{60}

輸出格式:

輸出需要包含 qq 行,每行包含 11 個非負整數,表示這名旅行者可以保留的最大幸運值。

輸入輸出樣例

輸入樣例#1:

4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4

輸出樣例#1:

14 
11

解題分析

顯然這玩意就是個樹上的線性基, 直接倍增維護貌似是不滿的O(Nlog2(G)log(N))O(Nlog^2(G)log(N)), 然而玄學ACAC了???

貌似還有點分的O(Nlog(G)log(N))O(Nlog(G)log(N))的做法, 以後填坑吧。

程式碼如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 20050
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
struct Edge {int to, nex;} edge[MX << 1];
ll base[MX][16][63], val[MX], ans[63], res;
int fa[MX][16], head[MX], dep[MX], lg[MX];
int dot, q, cnt;
IN void add(R int from, R int to)
{edge[++cnt] = {to, head[from]}, head[from] = cnt;}
IN void insert(ll *dat, ll val)
{
	for (R int i = 60; ~i; --i)
	{
		if(val & (1ll << i))
		{
			if(!dat[i]) return dat[i] = val, void();
			val ^= dat[i];
		}
	}
}
IN void merge(ll *down, ll *up)
{
	for (R int i = 60; ~i; --i)
	if(up[i]) insert(down, up[i]);
}
void DFS(R int now)
{
	R int tar, par;
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if(edge[i].to == fa[now][0]) continue;
		tar = edge[i].to;
		fa[tar][0] = now;
		dep[tar] = dep[now] + 1;
		insert(base[tar][0], val[now]);
		for (R int j = 1; j <= lg[dep[tar]]; ++j)
		{
			std::memcpy(base[tar][j], base[tar][j - 1], sizeof(base[tar][j]));
			par = fa[tar][j - 1]; if(!par) break;
			merge(base[tar][j], base[par][j - 1]);
			fa[tar][j] = fa[par][j - 1];
		}
		DFS(tar);
	}
}
IN void getans(R int x, R int y)
{
	if(dep[x] < dep[y]) std::swap(x, y);
	R int del = dep[x] - dep[y];
	insert(ans, val[x]); insert(ans, val[y]);
	for (R int i = 0; i <= lg[del]; ++i)
	if(del & (1 << i)) merge(ans, base[x][i]), x = fa[x][i];
	if(x == y) return;
	for (R int i = lg[dep[x]]; ~i; --i)
	{
		if(fa[x][i] != fa[y][i])
		merge(ans, base[x][i]), merge(ans, base[y][i]), x = fa[x][i], y = fa[y][i];
	}
	merge(ans, base[x][0]);
}
int main(void)
{
	int a, b;
	in(dot), in(q);
	for (R int i = 2; i <= dot; ++i) lg[i] = lg[i >> 1] + 1;
	for (R int i = 1; i <= dot; ++i) in(val[i]);
	for (R int i = 1; i < dot; ++i) in(a), in(b), add(a, b);
	DFS(1);
	W (q--)
	{
		in(a), in(b);
		std::memset(ans, 0, sizeof(ans));
		res = 0; getans(a, b);
		for (R int i = 61; ~i; --i) if((res ^ ans[i]) > res) res ^= ans[i];
		printf("%lld\n", res);
	}
}