HDU - 6191 Query on A Tree (可持久化字典树/字典树合并)

题目链接

题意:有一棵树,树根为1,树上的每个结点都有一个数字x。给出Q组询问,每组询问有两个值u,x,代表询问以结点u为根的子树中的某一个数与x的最大异或值。

解法一:dfs序+可持久化字典树。看到子树询问,首先要想到dfs序啦。可以对所有结点按dfs序依次建立可持久化的字典树,字典树上的每个结点除了要保存它的后继结点以外,还要保存这个结点出现的次数num。询问结点u时,对[bg[u],ed[u]]上的字典树的num做差,字典树上剩下的num>0的结点即为可行状态,然后按普通的字典树的查询方法查询就是了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=30;
int n,Q,head[N],nxt[N],to[N],val[N],nEdge;
int rt[N],num[N*32],go[N*32][2],nnode;
int bg[N],ed[N],tot;
void AddEdge(int u,int v) {
    nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
}

int update(int v,int x,int bit) {
    int u=++nnode;
    num[u]=num[v]+1;
    if(bit<0)return u;
    int t=(x>>bit)&1;
    go[u][t]=update(go[v][t],x,bit-1);
    go[u][t^1]=go[v][t^1];
    return u;
}

void dfs(int u) {
    bg[u]=++tot;
    rt[tot]=update(rt[tot-1],val[u],M);
    for(int e=head[u]; ~e; e=nxt[e]) {
        int v=to[e];
        dfs(v);
    }
    ed[u]=tot;
}

int query(int u,int v,int x,int bit,int now) {
    if(bit<0)return now;
    int t=(x>>bit)&1;
    if(go[u][t^1]-go[v][t^1]>0)
        return query(go[u][t^1],go[v][t^1],x,bit-1,now|(1<<bit));
    else return query(go[u][t],go[v][t],x,bit-1,now);
}

int main() {
    while(scanf("%d%d",&n,&Q)==2) {
        memset(head,-1,sizeof head);
        nEdge=nnode=tot=0;
        for(int i=1; i<=n; ++i)scanf("%d",&val[i]);
        for(int i=2; i<=n; ++i) {
            int u;
            scanf("%d",&u);
            AddEdge(u,i);
        }
        rt[0]=go[0][0]=go[0][1]=num[0]=0;
        dfs(1);
        while(Q--) {
            int u,x;
            scanf("%d%d",&u,&x);
            printf("%d\n",query(rt[ed[u]],rt[bg[u]-1],x,M,0));
        }
    }
    return 0;
}

解法二:离线+字典树合并。可以自底而上来回答询问,每回答完一个结点下的所有子节点的询问,就将这个结点的字典树与它所有子节点的字典树合并。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=30;
int n,Q,head[N],nxt[N],to[N],val[N],nEdge;
int rt[N],go[N*32][2],nnode,ans[N];
struct QUERY {
    int x,i;
};
vector<QUERY> qr[N];
void AddEdge(int u,int v) {
    nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
}

int build(int x,int bit) {
    int u=++nnode;
    if(bit<0)return u;
    int t=(x>>bit)&1;
    go[u][t]=build(x,bit-1);
    go[u][t^1]=0;
    return u;
}

int Merge(int u,int v,int bit) {
    if(!u)return v;
    if(!v)return u;
    if(bit<0)return u;
    go[u][0]=Merge(go[u][0],go[v][0],bit-1);
    go[u][1]=Merge(go[u][1],go[v][1],bit-1);
    return u;
}

int query(int u,int x,int bit,int now) {
    if(bit<0)return now;
    int t=(x>>bit)&1;
    if(go[u][t^1])
        return query(go[u][t^1],x,bit-1,now|(1<<bit));
    else return query(go[u][t],x,bit-1,now);
}

void dfs(int u) {
    for(int e=head[u]; ~e; e=nxt[e]) {
        int v=to[e];
        dfs(v);
    }
    for(int e=head[u]; ~e; e=nxt[e]) {
        int v=to[e];
        rt[u]=Merge(rt[u],rt[v],M);
    }
    for(int i=0; i<qr[u].size(); ++i) {
        ans[qr[u][i].i]=query(rt[u],qr[u][i].x,M,0);
    }
}

int main() {
    while(scanf("%d%d",&n,&Q)==2) {
        memset(head,-1,sizeof head);
        nEdge=nnode=0;
        for(int i=0; i<=n; ++i)qr[i].clear();
        for(int i=1; i<=n; ++i)scanf("%d",&val[i]);
        for(int i=2; i<=n; ++i) {
            int u;
            scanf("%d",&u);
            AddEdge(u,i);
        }
        for(int i=1; i<=n; ++i)rt[i]=build(val[i],M);
        for(int i=0; i<Q; ++i) {
            int u,x;
            scanf("%d%d",&u,&x);
            qr[u].push_back({x,i});
        }
        dfs(1);
        for(int i=0; i<Q; ++i)printf("%d\n",ans[i]);
    }
    return 0;
}

 还有一种解法是“可持久化字典树合并”,即父结点继承所有子结点的字典树,继承的方式与字典树合并一样,只不过把两棵树的公共部分开新结点就好,也是在线的,但空间消耗较大。

#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=30;
int n,Q,head[N],nxt[N],to[N],val[N],nEdge;
int rt[N],go[N*64][2],nnode,ans[N];
void AddEdge(int u,int v) {
    nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
}
int build(int x,int bit) {
    int u=++nnode;
    if(bit<0)return u;
    int t=(x>>bit)&1;
    go[u][t]=build(x,bit-1);
    go[u][t^1]=0;
    return u;
}

int Merge(int u,int v,int bit) {
    if(!u)return v;
    if(!v)return u;
    if(bit<0)return u;
    int w=++nnode;
    go[w][0]=Merge(go[u][0],go[v][0],bit-1);
    go[w][1]=Merge(go[u][1],go[v][1],bit-1);
    return w;
}
int query(int u,int x,int bit,int now) {
    if(bit<0)return now;
    int t=(x>>bit)&1;
    if(go[u][t^1])
        return query(go[u][t^1],x,bit-1,now|(1<<bit));
    else return query(go[u][t],x,bit-1,now);
}
void dfs(int u) {
    for(int e=head[u]; ~e; e=nxt[e]) {
        int v=to[e];
        dfs(v);
    }
    for(int e=head[u]; ~e; e=nxt[e]) {
        int v=to[e];
        rt[u]=Merge(rt[u],rt[v],M);
    }
}

int main() {
    while(scanf("%d%d",&n,&Q)==2) {
        memset(head,-1,sizeof head);
        nEdge=nnode=0;
        for(int i=1; i<=n; ++i)scanf("%d",&val[i]);
        for(int i=2; i<=n; ++i) {
            int u;
            scanf("%d",&u);
            AddEdge(u,i);
        }
        for(int i=1; i<=n; ++i)rt[i]=build(val[i],M);
        dfs(1);
        for(int i=0; i<Q; ++i) {
            int u,x;
            scanf("%d%d",&u,&x);
            printf("%d\n",query(rt[u],x,M,0));
        }
    }
    return 0;
}

 

posted @ 2018-11-24 13:11  jrltx  阅读(272)  评论(0编辑  收藏  举报