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;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13602474.html