好巧妙啊,感觉从来没有用过按位dp的trick,也没有用过树上链分块的trick
挂个链,全程看他的思路写的,当然lych帮我理解了最难懂的一部分
首先这里有个玄学的分块
每个点统计它上面256(其实差不多就是n^0.5)个点的情况
但是发现不同的块需要不同的处理,因为不同的块在第9位及以上的位会产生影响
所以对于每个点都需要预处理出Fi,也就是当这个块是统计答案时的第i个块时的最大答案
那么在跳的时候只要疯狂的跳256,同时记录下当前这一块已经是统计的第几块,最后不到256个暴力判一下
然而
暴力预处理是n*(256)^2≈n^2的
gg
于是可以用一个类似dp的方法做出来
首先把每一个答案找一个最好的地方放好(就是块内的每一个点所对应的答案找一个i使答案的9~16位为11111111)
接下来就一定是这些答案通过与下标同异或一个东西所得到的答案了
先枚举位,考虑这些dp值在这一位取反对答案的贡献
没了(感性理解是对的)
1 #include <bits/stdc++.h> 2 #define KUAI 256 3 #define LOG 8 4 #define FULL 255 5 using namespace std; 6 int n,m,E,x,y; 7 int dep[50001],fa[50001],Fa[50001],ans[50001][300],a[50001],fir[50001],nex[100001],to[100001]; 8 void add(int x,int y) 9 { 10 nex[++E]=fir[x];fir[x]=E;to[E]=y; 11 } 12 void build(int now,int fat) 13 { 14 if(now==5) 15 int e=1; 16 dep[now]=dep[fat]+1; 17 fa[now]=fat; 18 for(int i=0,x=now;i<=KUAI && x;i++,x=fa[x]) 19 if(i==KUAI) 20 Fa[now]=x; 21 else 22 { 23 int y=a[x]^i;//当前答案 24 ans[now][FULL^(y>>LOG)]=max(ans[now][FULL^(y>>LOG)],y|(FULL<<LOG));//在最好的地方放好 25 } 26 for(int i=0;i<LOG;i++) 27 for(int j=0;j<KUAI;j++) 28 if(ans[now][j]==0) 29 if(ans[now][j^(1<<i)]) 30 ans[now][j]=ans[now][j^(1<<i)]^(1<<(LOG+i)); 31 for(int i=fir[now];i;i=nex[i]) 32 if(to[i]!=fa[now]) 33 build(to[i],now); 34 } 35 int main() 36 { 37 scanf("%d%d",&n,&m); 38 for(int i=1;i<=n;i++) 39 scanf("%d",&a[i]); 40 for(int i=1;i<n;i++) 41 scanf("%d%d",&x,&y), 42 add(x,y),add(y,x); 43 build(1,0); 44 for(int i=1;i<=m;i++) 45 { 46 scanf("%d%d",&x,&y); 47 if(i==2) 48 int e=1; 49 swap(x,y); 50 int ret=a[y]^(dep[x]-dep[y]),j=0,de=0; 51 for(;dep[x]-dep[y]>=KUAI;j++,de+=KUAI) 52 ret=max(ret,ans[x][j]),x=Fa[x]; 53 for(;x!=y;x=fa[x],de++) 54 ret=max(ret,a[x]^de); 55 printf("%d\n",ret); 56 } 57 return 0; 58 }
时间: 2024-10-25 12:19:40