BZOJ 4568 4568: [Scoi2016]幸运数字 (线性基+树链剖分+线段树)
Posted konjak魔芋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 4568 4568: [Scoi2016]幸运数字 (线性基+树链剖分+线段树)相关的知识,希望对你有一定的参考价值。
4568: [Scoi2016]幸运数字
Description
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。Input
第一行包含 2 个正整数 n ,q,分别表示城市的数量和旅行者数量。第二行包含 n 个非负整数,其中第 i 个整数 Gi 表示 i 号城市的幸运值。随后 n-1 行,每行包含两个正整数 x ,y,表示 x 号城市和 y 号城市之间有一条道路相连。随后 q 行,每行包含两个正整数 x ,y,表示这名旅行者的旅行计划是从 x 号城市到 y 号城市。N<=20000,Q<=200000,Gi<=2^60Output
输出需要包含 q 行,每行包含 1 个非负整数,表示这名旅行者可以保留的最大幸运值。
Sample Input
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4Sample Output
14
11HINT
Source
【分析】
看看时限就知道怎么做了。。
线段树维护区间的线性基。
区间合并线性基,就一个个元素插进去就可以了。【操作带一个常数60
树的话就剖一下。
然后用线性基的性质求异或和的最大值,就是从高位开始枚举,若^所得比ans大就把它异或进去。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 20010 8 #define LL long long 9 10 LL a[Maxn],aa[Maxn]; 11 12 struct nnode 13 { 14 int x,y,next; 15 }t[Maxn*2]; 16 int len,first[Maxn]; 17 18 void ins(int x,int y) 19 { 20 t[++len].x=x;t[len].y=y; 21 t[len].next=first[x];first[x]=len; 22 } 23 24 int fa[Maxn],son[Maxn],sm[Maxn],dep[Maxn]; 25 void dfs1(int x,int ff) 26 { 27 son[x]=0;fa[x]=ff;sm[x]=1; 28 dep[x]=dep[ff]+1; 29 for(int i=first[x];i;i=t[i].next) if(t[i].y!=ff) 30 { 31 int y=t[i].y; 32 dfs1(y,x); 33 sm[x]+=sm[y]; 34 if(son[x]==0||sm[y]>sm[son[x]]) son[x]=y; 35 } 36 } 37 38 int dfn[Maxn],tp[Maxn],cnt; 39 void dfs2(int x,int tpp) 40 { 41 tp[x]=tpp;dfn[x]=++cnt; 42 aa[cnt]=a[x]; 43 if(son[x]) dfs2(son[x],tpp); 44 for(int i=first[x];i;i=t[i].next) if(t[i].y!=fa[x]&&t[i].y!=son[x]) 45 { 46 int y=t[i].y; 47 dfs2(y,y); 48 } 49 } 50 51 struct node 52 { 53 int l,r,lc,rc; 54 LL w[61]; 55 }tr[Maxn*2]; 56 57 void ist(int x,LL y) 58 { 59 for(int i=60;i>=0;i--) if((1LL<<i)&y) 60 { 61 if(tr[x].w[i]) y^=tr[x].w[i]; 62 else {tr[x].w[i]=y;break;} 63 } 64 } 65 66 void merge(int x,int y) 67 { 68 for(int i=0;i<=60;i++) if(tr[y].w[i]!=0) ist(x,tr[y].w[i]); 69 } 70 71 int tot; 72 int build(int l,int r) 73 { 74 int x=++tot; 75 tr[x].l=l;tr[x].r=r; 76 memset(tr[x].w,0,sizeof(tr[x].w)); 77 if(l!=r) 78 { 79 int mid=(l+r)>>1; 80 tr[x].lc=build(l,mid); 81 tr[x].rc=build(mid+1,r); 82 merge(x,tr[x].lc); 83 merge(x,tr[x].rc); 84 } 85 else 86 { 87 tr[x].lc=tr[x].rc=0; 88 ist(x,aa[l]); 89 } 90 return x; 91 } 92 93 void query(int x,int l,int r) 94 { 95 if(tr[x].l==l&&tr[x].r==r) 96 { 97 merge(0,x); 98 return; 99 } 100 int mid=(tr[x].l+tr[x].r)>>1; 101 if(r<=mid) query(tr[x].lc,l,r); 102 else if(l>mid) query(tr[x].rc,l,r); 103 else 104 { 105 query(tr[x].lc,l,mid); 106 query(tr[x].rc,mid+1,r); 107 } 108 } 109 110 LL fquery(int x,int y) 111 { 112 memset(tr[0].w,0,sizeof(tr[0].w)); 113 while(tp[x]!=tp[y]) 114 { 115 if(dep[tp[x]]<dep[tp[y]]) swap(x,y); 116 query(1,dfn[tp[x]],dfn[x]); 117 x=fa[tp[x]]; 118 } 119 if(dep[x]<dep[y]) swap(x,y); 120 query(1,dfn[y],dfn[x]); 121 LL ans=0; 122 for(int i=60;i>=0;i--) if((ans^tr[0].w[i])>ans) ans^=tr[0].w[i]; 123 return ans; 124 } 125 126 int main() 127 { 128 int n,q; 129 scanf("%d%d",&n,&q); 130 len=0; 131 memset(first,0,sizeof(first)); 132 for(int i=1;i<=n;i++) scanf("%lld",&a[i]); 133 for(int i=1;i<n;i++) 134 { 135 int x,y; 136 scanf("%d%d",&x,&y); 137 ins(x,y);ins(y,x); 138 } 139 dep[0]=0; 140 dfs1(1,0);cnt=0; 141 dfs2(1,1);tot=0; 142 build(1,n); 143 for(int i=1;i<=q;i++) 144 { 145 int x,y; 146 scanf("%d%d",&x,&y); 147 printf("%lld\\n",fquery(x,y)); 148 } 149 return 0; 150 }
2017-03-13 14:06:24
以上是关于BZOJ 4568 4568: [Scoi2016]幸运数字 (线性基+树链剖分+线段树)的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4568: [Scoi2016]幸运数字(LCA+线性基)
[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)