1. 程式人生 > >[SCOI2016]幸運數字

[SCOI2016]幸運數字

next pac 選擇性 輸入輸出格式 XML 訪問 格式 margin ram

題目描述

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]幸運數字