[SCOI2016]幸运数字

Posted Z-Y-Y-S

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 个非负整数,表示这名旅行者可以保留的最大幸运值。

输入输出样例

输入样例#1:
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4
输出样例#1:
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 }

 

以上是关于[SCOI2016]幸运数字的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4568: [Scoi2016]幸运数字

BZOJ4568: [Scoi2016]幸运数字

[SCOI2016]幸运数字

[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)

[SCOI2016]幸运数字

bzoj4568 [Scoi2016]幸运数字