HDU5841 Alice and Bob (Trie+启发式合并)
这题首先理解题意想暴力,在一个子树中,A想要两点异或值最大,B想要异或最小,A先选
这其实就是告诉我们,A要选择一个点,使得他和其他点的异或值的最小值最大。
对于异或贪心取值,一般来说,很容易想到使用01Trie上贪心。但是我们要求每个子树上的情况,暴力显然是平方级别。
这个时候,我们遇到的问题是,统计树上信息且不带修改,因此想到可以用启发式合并,通过子树的信息来更新父节点的信息。
对于更新来说,就是merge函数,merge函数也是递归到底部后回溯,按位维护,通过低位更新高位,具体方法可以看代码注释
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<ll,ll> pll; const int N=2e5+10; const int M=2e6+10; const int inf=0x3f3f3f3f; const int mod=772002; int h[N],ne[N],e[N],idx; int n,tr[M][2],rt[N]; int w[N],times; int ans[N]; int p[M],val[M]; int sum[M]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } int query(int u,int k,int dep){ if(dep==-1) return val[u]; int sign=(k>>dep)&1; if(tr[u][sign]) return query(tr[u][sign],k,dep-1); else return query(tr[u][sign^1],k,dep-1); } void pushup(int u,int dep){ int l=tr[u][0],r=tr[u][1]; sum[u]=sum[l]+sum[r]; if(sum[l]==1&&sum[r]==1){//只能取这两个点 p[u]=p[l]^p[r]; } else if(!sum[l]||!sum[r]){//只能取存在的一遍 p[u]=sum[l]?p[l]:p[r]; } else if(sum[l]>1&&sum[r]>1){//对于alice来说,取大的是优的 p[u]=max(p[l],p[r]); } else{//取最小的值,才是答案 if(sum[l]!=1) swap(l,r); p[u]=p[l]^query(r,p[l],dep-1); } } void insert(int &u,int dep,int k){ int i; if(!u) u=++times; if(dep==-1){ sum[u]=1; p[u]=val[u]=k; return ; } if(k>>dep&1) insert(tr[u][1],dep-1,k); else insert(tr[u][0],dep-1,k); pushup(u,dep); } int Merge(int u,int v,int dep){ int i; if(!u||!v) return u|v; if(dep==-1){ sum[u]+=sum[v];//最后一位更新数据 if(sum[u]>1){ p[u]=0; } else{ p[u]=val[u]; } return u; } tr[u][0]=Merge(tr[u][0],tr[v][0],dep-1); tr[u][1]=Merge(tr[u][1],tr[v][1],dep-1); pushup(u,dep); return u; } void dfs(int u,int fa){ insert(rt[u],16,w[u]); int i; for(i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(j==fa) continue; dfs(j,u); rt[u]=Merge(rt[u],rt[j],16); } if(sum[rt[u]]>1) ans[u]=p[rt[u]]; } void init(){ int i; times=0,idx=0; memset(h,-1,sizeof h); memset(sum,0,sizeof sum); memset(ans,-1,sizeof ans); memset(rt,0,sizeof rt); memset(tr,0,sizeof tr); } int main(){ ios::sync_with_stdio(false); int t; cin>>t; int cnt=1; while(t--){ cin>>n; int i; init(); for(i=1;i<=n;i++){ cin>>w[i]; } for(i=1;i<n;i++){ int a,b; cin>>a>>b; add(a,b); add(b,a); } dfs(1,-1); int m; cin>>m; cout<<"Case #"<<cnt++<<":"<<endl; while(m--){ int u; cin>>u; cout<<ans[u]<<endl; } } return 0; }
没有人不辛苦,只有人不喊疼