[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
我们每次找到重心,处理与重心相关的路径。
处理时我们将重心到每个节点这一段的线性基存起来,然后找到所有经过重心的路径
在遍历以重心G为根的一个子树过程中,找到与这棵子树中节点u有关的询问(u,v),判断是否在之前遍历过的以重心为根的其他子树中出现过,如果出现过,我们可以将G->u和G->v的线性基合并
找到合并后的线性基中的最大值就好了。对于合并,直接暴力拆开一个线性基,一个一个插入到另一个线性基中
处理完这棵子树之后,再遍历一遍,打上访问过的标记。这样保证所有询问都只计算过一次。
注意,处理完这个重心之后,清空标记时,不要memset,直接再遍历一遍就好了,快得多。
值得注意的是,要单独考虑与G有关的询问如(G,v)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 struct Node 9 { 10 int next,to,dis; 11 }edge[40001],edgeq[400001]; 12 int num,numq,head[20001],headq[200001]; 13 int size[20001],maxsize[20001],minsize,root,n,q; 14 bool vis[20001],judge[20001]; 15 lol ans[200001],pw[64],val[20001]; 16 struct BASE 17 { 18 lol a[64]; 19 void clear() 20 { 21 for (int i=0;i<=62;i++) 22 a[i]=0; 23 } 24 void insert(lol x) 25 { 26 for (int i=62;i>=0;i--) 27 { 28 if (x&pw[i]) 29 { 30 if (a[i]==0) 31 {a[i]=x;break;} 32 else x^=a[i]; 33 } 34 } 35 } 36 lol getmax() 37 { 38 lol x=0; 39 for (int i=62;i>=0;i--) 40 { 41 if ((x^a[i])>x) 42 x^=a[i]; 43 } 44 return x; 45 } 46 void merge(BASE b) 47 { 48 for (int i=0;i<=62;i++) 49 insert(b.a[i]); 50 } 51 void copy(BASE b) 52 { 53 for (int i=0;i<=62;i++) 54 a[i]=b.a[i]; 55 } 56 }base[20002]; 57 void add(int u,int v) 58 { 59 num++; 60 edge[num].next=head[u]; 61 head[u]=num; 62 edge[num].to=v; 63 } 64 void addq(int u,int v,int d) 65 { 66 numq++; 67 edgeq[numq].next=headq[u]; 68 headq[u]=numq; 69 edgeq[numq].to=v; 70 edgeq[numq].dis=d; 71 } 72 void get_size(int x,int fa) 73 { 74 int i; 75 size[x]=1; 76 maxsize[x]=0; 77 for (i=head[x];i;i=edge[i].next) 78 { 79 int v=edge[i].to; 80 if (vis[v]==0&&v!=fa) 81 { 82 get_size(v,x); 83 size[x]+=size[v]; 84 maxsize[x]=max(maxsize[x],size[v]); 85 } 86 } 87 } 88 void get_root(int r,int x,int fa) 89 { 90 int i; 91 maxsize[x]=max(maxsize[x],size[r]-size[x]); 92 if (maxsize[x]<minsize) 93 { 94 root=x; 95 minsize=maxsize[x]; 96 } 97 for (i=head[x];i;i=edge[i].next) 98 { 99 int v=edge[i].to; 100 if (vis[v]==0&&v!=fa) 101 { 102 get_root(r,v,x); 103 } 104 } 105 } 106 void get_ans(int r,int x,int fa) 107 {int i; 108 base[x].copy(base[fa]); 109 base[x].insert(val[x]); 110 for (i=headq[x];i;i=edgeq[i].next) 111 { 112 int v=edgeq[i].to; 113 if (judge[v]) 114 { 115 BASE tmp; 116 tmp.copy(base[x]); 117 tmp.merge(base[v]); 118 ans[edgeq[i].dis]=tmp.getmax(); 119 } 120 else if (v==r) 121 { 122 ans[edgeq[i].dis]=base[x].getmax(); 123 } 124 } 125 for (i=head[x];i;i=edge[i].next) 126 { 127 int v=edge[i].to; 128 if (vis[v]==0&&v!=fa) 129 { 130 get_ans(r,v,x); 131 } 132 } 133 } 134 void get_update(int x,int fa) 135 { 136 judge[x]=!judge[x]; 137 for (int i=head[x];i;i=edge[i].next) 138 { 139 int v=edge[i].to; 140 if (vis[v]==0&&v!=fa) 141 { 142 get_update(v,x); 143 } 144 } 145 } 146 void doit(int x) 147 { 148 minsize=2e9; 149 get_size(x,0); 150 get_root(x,x,0); 151 vis[root]=1; 152 base[root].clear(); 153 base[root].insert(val[root]); 154 for (int i=head[root];i;i=edge[i].next) 155 { 156 int v=edge[i].to; 157 if (vis[v]==0) 158 { 159 get_ans(root,v,root); 160 get_update(v,root); 161 } 162 } 163 for (int i=head[root];i;i=edge[i].next) 164 { 165 int v=edge[i].to; 166 if (vis[v]==0) 167 { 168 get_update(v,root); 169 } 170 } 171 for (int i=head[root];i;i=edge[i].next) 172 { 173 int v=edge[i].to; 174 if (vis[v]==0) 175 { 176 doit(v); 177 } 178 } 179 } 180 int main() 181 {int i,u,v; 182 cin>>n>>q; 183 pw[0]=1; 184 for (i=1;i<=62;i++) 185 pw[i]=pw[i-1]*2; 186 for (i=1;i<=n;i++) 187 { 188 scanf("%lld",&val[i]); 189 } 190 for (i=1;i<=n-1;i++) 191 { 192 scanf("%d%d",&u,&v); 193 add(u,v);add(v,u); 194 } 195 for (i=1;i<=q;i++) 196 { 197 scanf("%d%d",&u,&v); 198 if (u!=v) 199 addq(u,v,i),addq(v,u,i); 200 else ans[i]=val[u]; 201 } 202 doit(1); 203 for (i=1;i<=q;i++) 204 { 205 printf("%lld\n",ans[i]); 206 } 207 }