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