【bzoj4568】[Scoi2016]幸运数字 树上倍增+高斯消元动态维护线性基
题目描述
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。
输入
第一行包含 2 个正整数 n ,q,分别表示城市的数量和旅行者数量。第二行包含 n 个非负整数,其中第 i 个整数 Gi 表示 i 号城市的幸运值。随后 n-1 行,每行包含两个正整数 x ,y,表示 x 号城市和 y 号城市之间有一条道路相连。随后 q 行,每行包含两个正整数 x ,y,表示这名旅行者的旅行计划是从 x 号城市到 y 号城市。N<=20000,Q<=200000,Gi<=2^60
输出
输出需要包含 q 行,每行包含 1 个非负整数,表示这名旅行者可以保留的最大幸运值。
样例输入
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4
样例输出
14
11
题解
自己yy出的树上倍增+高斯消元动态维护线性基
求最大异或和,显然需要使用线性基。那么对于每组询问,拿出路径上的线性基即可。
采用树上倍增的方法,设$f[i][x]$为x的$2^i$祖先,$a[i][x]$为x(包括)到$f[i][x]$(不包括)的路径上的点的线性基。
那么需要做的就是合并线性基。这里将小的暴力插入到大的中。对于插入的一个数,如果不能用原来的线性基把它消掉,那么就添加该数,并使用插入排序维护线性基单调递减。
最后贪心求解即可。
复杂度为感人的$O(q\log n\log^2g)$,实际跑了20s--
另外本题维护线性基如果使用vector则会MLE,所以必须使用数组。
#include <cstdio> #include <cstring> #include <algorithm> #define N 20010 using namespace std; typedef long long ll; struct data { ll v[64]; int tot; data() {memset(v , 0 , sizeof(v)) , tot = 0;} void insert(ll x) { int i; for(i = 0 ; i < tot ; i ++ ) if((x ^ v[i]) < x) x ^= v[i]; if(x) { v[++tot] = x; for(i = tot ; i ; i -- ) { if(v[i] > v[i - 1]) swap(v[i] , v[i - 1]); else break; } } } ll query() { ll ans = 0; int i; for(i = 0 ; i < tot ; i ++ ) if((ans ^ v[i]) > ans) ans ^= v[i]; return ans; } }a[15][N]; ll w[N]; int head[N] , to[N << 1] , next[N << 1] , cnt , fa[15][N] , deep[N] , log[N]; void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void merge(data a , data b , data *c) { if(a.tot > b.tot) swap(a , b); c->tot = b.tot; int i; for(i = 0 ; i < b.tot ; i ++ ) c->v[i] = b.v[i]; for(i = 0 ; i < a.tot ; i ++ ) c->insert(a.v[i]); } void dfs(int x) { int i; a[0][x].v[a[0][x].tot ++ ] = w[x]; for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[i][x] = fa[i - 1][fa[i - 1][x]] , merge(a[i - 1][x] , a[i - 1][fa[i - 1][x]] , &a[i][x]); for(i = head[x] ; i ; i = next[i]) if(to[i] != fa[0][x]) fa[0][to[i]] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]); } ll solve(int x , int y) { data tmp; int i; if(deep[x] < deep[y]) swap(x , y); for(i = log[deep[x] - deep[y]] ; ~i ; i -- ) if(deep[x] - (1 << i) >= deep[y]) merge(tmp , a[i][x] , &tmp) , x = fa[i][x]; for(i = log[deep[x]] ; ~i ; i -- ) if(deep[x] >= (1 << i) && fa[i][x] != fa[i][y]) merge(tmp , a[i][x] , &tmp) , merge(tmp , a[i][y] , &tmp) , x = fa[i][x] , y = fa[i][y]; if(x != y) merge(tmp , a[0][x] , &tmp) , merge(tmp , a[0][y] , &tmp) , x = fa[0][x]; tmp.insert(w[x]); return tmp.query(); } int main() { int n , m , i , x , y; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &w[i]); for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x); for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1; deep[1] = 1 , dfs(1); while(m -- ) scanf("%d%d" , &x , &y) , printf("%lld\n" , solve(x , y)); return 0; }