【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 个非负整数,表示这名旅行者可以保留的最大幸运值。

题意:

一棵树,节点有权值,求出一个节点到另一个节点的路径上的异或最大值。

题解:

①要求出多个数异或max还是用线性基好一点,用倍增加速线性基;

②先倍增预处理lca,同时预处理出lca移动路径上的节点线性基;

③查询先求出lca,然后像这样,类似于rmq的思想,暴力合并四个线性基:
用一些位运算技巧可以算出每个数的最高二进制有效位数和线性基的sz(是否满)可以优化掉很多常数,但这样好像已经可以过了。

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 const int N = 20010;
 7 int n,m,f[N][20],hd[N],o,lg[N],p[20],lwt[N],dep[N];
 8 ll w[N];
 9 struct Edge{int v,nt;}E[2*N];
10 int cal(ll x){
11     int ret=0;
12     for(int i=32;i;i>>=1)if(x>>i)x>>=i,ret|=i;
13     return ret;
14 }
15 struct basis{
16     ll d[61];
17     void ins(ll x){
18         for(int i = cal(x);i >= 0;i--)
19         if((x>>i)&1){
20             if(!d[i]) {d[i] = x;break;}
21             else x^=d[i]; 
22         }
23     }
24     ll mx(){
25         ll ret = 0;
26         for(int i = 60;i >= 0;i--) if(d[i]) ret = max(ret,ret^d[i]);
27         return ret;
28     }
29     void merge(basis a){
30         for(int i = 60;i >= 0;i--) if(a.d[i]) ins(a.d[i]);
31     }
32 }Bs[N][20],tmp;
33 char gc(){
34     static char *p1,*p2,s[1000000];
35     if(p1==p2) p2=(p1=s)+fread(s,1,1000000,stdin);
36     return(p1==p2)?EOF:*p1++;
37 }
38 ll rd(){
39     ll x = 0; char c = gc();
40     while(c<'0'||c>'9') c = gc();
41     while(c>='0'&&c<='9') x=x*10+c-'0',c = gc();
42     return x;
43 }
44 void adde(int u,int v){
45     E[o] = (Edge){v,hd[u]}; hd[u] = o++;
46     E[o] = (Edge){u,hd[v]}; hd[v] = o++;
47 }
48 void dfs(int u,int fa){
49     dep[u]=dep[fa]+1; f[u][0] = fa; Bs[u][0].ins(w[fa]);
50     for(int i = 1;i <= 15 && p[i]<dep[u];i++){
51         f[u][i] = f[f[u][i-1]][i-1];
52         Bs[u][i] = Bs[u][i-1];
53         Bs[u][i].merge(Bs[f[u][i-1]][i-1]);
54     }
55     for(int i = hd[u],v;i!=-1;i=E[i].nt)if((v=E[i].v)!=fa) dfs(v,u);
56 }
57 int get(int u,int v){
58     if(dep[u]<dep[v]) swap(u,v);
59     for(int i = dep[u]-dep[v];i;i-=lwt[i]) u = f[u][lg[lwt[i]]];
60     if(u==v) return u;
61     for(int i = 15;i >= 0;i--)if(f[u][i]!=f[v][i]){u = f[u][i]; v = f[v][i];}
62     return f[u][0];
63 }
64 void query(int u,int v){
65     int lca = get(u,v); 
66     memset(tmp.d,0,sizeof(tmp.d));
67     tmp.ins(w[u]),tmp.ins(w[v]);
68     if(u!=lca){
69         int lu = dep[u] - dep[lca];
70         tmp.merge(Bs[u][lg[lu]]);
71         for(int i = lu-p[lg[lu]];i;i-=lwt[i]) u = f[u][lg[lwt[i]]]; 
72         tmp.merge(Bs[u][lg[lu]]);
73     }
74     if(v!=lca){
75         int lv = dep[v] - dep[lca]; 
76         tmp.merge(Bs[v][lg[lv]]);
77         for(int i = lv-p[lg[lv]];i;i-=lwt[i]) v = f[v][lg[lwt[i]]];
78         tmp.merge(Bs[v][lg[lv]]);
79     }
80     ll ans = tmp.mx();
81     printf("%lld\n",ans);
82 }
83 int main()
84 {    freopen("bzoj4568.in","r",stdin);
85     freopen("bzoj4568.out","w",stdout); 
86     n = rd(); m = rd();
87     lg[0] = -1;
88     for(int i = 1;i <= n;i++) w[i]=rd(),hd[i] = -1,lg[i]=lg[i>>1]+1,lwt[i]=i&(-i);
89     for(int i = p[0] = 1;i <= 15;i++) p[i] = p[i-1]*2;
90     for(int i = 1;i < n;i++) adde(rd(),rd());
91     dfs(1,0);
92     for(int i = 1;i <= m;i++) {
93         int u = rd(),v = rd();
94         query(u,v);
95         //printf("%d\n",i);
96     }
97     //printf("haha\n");
98     return 0;
99 }//by tkys_Austin;

 

 

 

 

posted @ 2018-03-26 07:10  大米饼  阅读(231)  评论(0编辑  收藏  举报