题意:给你一棵树,每次询问一个数与结点u的子树上的结点的值异或的最大值。
要查询一组数和一个数异或的最大值,很轻易可以看出是字典树。(01字典树)
然而每次要查询的是以某一个结点为根的子树上的数值的异或最大值,当然不能建n棵以每一个结点为根的字典树,所以我们用到了启发式合并,这样我们先查询后合并,就可以得到所有查询的答案。
像字典树线段树这种树结构,如果两颗树a,b的结构相同,那么他们就可以合并。(线段树的启发式合并)
#include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 1e5 + 10; void tran(char *a,int x) { int i; for(i=0;i<=31;i++) a[i] = '0'; a[i] ='\0'; i = 0; while(x) { a[i++] = (x%2)+'0'; x /= 2; } } struct trie { trie *next[2]; int cnt; trie() { memset(next,0,sizeof(next)); cnt = 0; } }; trie *root[maxn]; void insert(trie *p,int x) { int i,k; char s[33]; tran(s,x); for(i=31;i>=0;i--) { k=s[i]-'0'; if(p->next[k]==NULL) p->next[k]=new trie(); p=p->next[k]; } p->cnt = x; } int query(trie *p,int x,int dir)//dir为1就是往相反的方向走,也就是求最大值 { char s[33]; tran(s,x); for(int i=31;i>=0;i--) { int x = s[i] - '0'; x = x^dir; if(p->next[x] == NULL) x^=1; p = p->next[x]; } return (p->cnt) ^ x; } void del(trie *p) { for(int i=0;i<2;i++) if(p->next[i]!=NULL) del(p->next[i]); free(p); } trie* Merge(trie *a,trie *b) { if(a == NULL) return b; if(b == NULL) return a; a->next[0] = Merge(a->next[0],b->next[0]); a->next[1] = Merge(a->next[1],b->next[1]); free(b); return a; } struct Query { int id,x; Query(int _id,int _x) { id = _id; x = _x; } }; int a[maxn]; vector<int> e[maxn]; vector<Query> q[maxn]; int ans[maxn]; void dfs(int x,int pre) { root[x] = new trie(); insert(root[x],a[x]); for(int i=0;i<e[x].size();i++) { int xx = e[x][i]; if(xx == pre) continue; dfs(xx,x); Merge(root[x],root[xx]); } for(int i=0;i<q[x].size();i++) { ans[q[x][i].id] = query(root[x],q[x][i].x,1); } } int main(void) { int n,m,i,j; while(scanf("%d%d",&n,&m)==2) { for(i=1;i<=n;i++) { e[i].clear(); q[i].clear(); scanf("%d",&a[i]); } for(i=2;i<=n;i++) { int x; scanf("%d",&x); e[x].push_back(i); } for(i=1;i<=m;i++) { int u,x; scanf("%d%d",&u,&x); q[u].push_back(Query(i,x)); } dfs(1,-1); for(i=1;i<=m;i++) printf("%d\n",ans[i]); del(root[1]); } return 0; }