[TJOI2018]异或
这大概是这个题最蒻的一篇题解了吧,供不会可持久化$Trie$且在解决本题之前不想学会的人食用
并不会可持久化$Trie$的蒟蒻本蒻遇到了这个题,然后用$Trie$维护的树链剖分水过去了
数集中的数与询问的异或最大值求解思路
思路很常见,比较好想,对数集中的所有数从高位到低位建立$01Trie$,然后贪心求解即可——设目前匹配到从高到低的第$i$位,若询问$x$的第$i$位为0,则优先进入到$Trie$树中当前节点的1方向,意义为在原最优解集上选择了所有第$i$位为1的数构成的子集作为新的最优解集,答案增加$2^{i-1}$,若当前节点的1方向不存在(原最优解集中不存在第$i$位为1的数)则进入0方向,答案不更新,询问$x$的第$i$位为1则相反,不再赘述。由进制的性质可得,贪心策略的正确性显然
考虑本题
子树问题常常通过$dfs$序解决,链上问题常常通过树链剖分解决,而树链剖分在其执行过程中维护了$dfs$序,故考虑通过树链剖分解决问题
树链剖分的查询按$dfs$序是区间连续的,故只需维护$Trie$上的每个节点对应的数集(以每个节点的$dfs$序为代表),对于每个查询在数集上进行二分,判断查询区间内$0/1$方向的节点的存在性即可
时间复杂度会比可持久化$Trie$多一个$log$,不过也是可过的
代码
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; const int maxn=1e5+10; struct edge{ int next,to; }e[maxn*2]; vector<int>vc[maxn*30]; int n,m,v[maxn],head[maxn],cnt,f[maxn],d[maxn],son[maxn]; int tr[maxn*30][2],size[maxn],top[maxn],id[maxn],rk[maxn]; void insert(int x,int id) { for(int now=0,i=29;i>=0;i--) { int c=(x&(1<<i))?1:0; now=tr[now][c]?tr[now][c]:tr[now][c]=++cnt; vc[now].push_back(id); } } int query(int x,int y,int k) { int ret=0; for(int now=0,i=29;i>=0;i--) { int to=(k&(1<<i))?0:1,trs=tr[now][to]; if(upper_bound(vc[trs].begin(),vc[trs].end(),y)-lower_bound(vc[trs].begin(),vc[trs].end(),x)) now=tr[now][to],ret+=(1<<i); else now=tr[now][to^1]; } return ret; } void add(int x,int y) { e[++cnt].next=head[x]; e[cnt].to=y; head[x]=cnt; } void dfs1(int x) { size[x]=1,d[x]=d[f[x]]+1; for(int v,i=head[x];i;i=e[i].next) if((v=e[i].to)!=f[x]) { f[v]=x,dfs1(v),size[x]+=size[v]; if(size[son[x]]<size[v]) son[x]=v; } } void dfs2(int x,int tp) { top[x]=tp,id[x]=++cnt,rk[cnt]=x; if(son[x]) dfs2(son[x],tp); for(int v,i=head[x];i;i=e[i].next) if((v=e[i].to)!=f[x]&&v!=son[x]) dfs2(v,v); } int sum(int x,int y,int k) { int ret=0; while(top[x]!=top[y]) { if(d[top[x]]<d[top[y]]) swap(x,y); ret=max(ret,query(id[top[x]],id[x],k)); x=f[top[x]]; } if(id[x]>id[y]) swap(x,y); return ret=max(ret,query(id[x],id[y],k)); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int x,y,i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y),add(y,x); } cnt=0,dfs1(1),dfs2(1,1),cnt=0; for(int i=1;i<=n;i++) insert(v[rk[i]],i); for(int op,x,y,k,i=1;i<=m;i++) { scanf("%d%d%d",&op,&x,&y); if(op==1) printf("%d\n",query(id[x],id[x]+size[x]-1,y)); else { scanf("%d",&k); printf("%d\n",sum(x,y,k)); } } return 0; }