首页 > 其他 > 详细

做题集——(字典树+启发式合并+dfs)Query on A Tree

时间:2021-05-08 23:30:18      阅读:21      评论:0      收藏:0      [点我收藏+]

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=6191

题面:

技术分享图片

Sample Input

2 2
1 2
1
1 3
2 1

Output

2
3

题意:

  给定一棵树,已知其所有节点所含的值,开始进行询问,每次询问给定两个值(a, b),求子树a的节点中与b异或值最大的结果

思路:

  求给定树中的最大异或值,不难想到要使用字典树的方法,那么接下来就不难想到使用合并的方法从子节点得到父节点的字典树。即整道题可以使用dfs遍历所有节点,遍历的过程中构筑字典树,过程中得到答案并将其存储,最后进行输出。

Code:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
//dfs所有节点, 得到每一个节点下如果包含问题的异或最大值, 使用u表示同一个节点
//
const int MAXN = 1e5 + 7;
vector<int> E[MAXN];
int val[MAXN], ans[MAXN];
vector<pair<int, int> > ver[MAXN];
//用于建立字典树, 由于编号是乱序的, 所以可以全局共用
int nxt[MAXN * 32][2];
int Unuse[MAXN * 32], cnt, tot;
void Init(int n) {
    for (int i = 1; i <= n; i++) {
        E[i].clear();
        ver[i].clear();
    }
    cnt = tot = 0;
}
struct Trie {
    int root;
    int NewNode() {
        int rear;
        if (cnt) rear = Unuse[cnt--];
        else rear = ++tot;
        memset(nxt[rear], 0, sizeof(nxt[rear]));    //因为回收的节点其"指针"没有清除, 要除掉
        return rear;
    }
    void Init() {
        root = NewNode();
    }
    void Rec(int u) {
        if (nxt[u][0]) Rec(nxt[u][0]);
        if (nxt[u][1]) Rec(nxt[u][1]);
        Unuse[++cnt] = u;
    }
    void Insert(int x) {
        int now = root, tmp;
        for (int i = 31; i >= 0; i--) {
            tmp = (x >> i) & 1;
            if (!nxt[now][tmp]) nxt[now][tmp] = NewNode();
            now = nxt[now][tmp];
        }
        //遍历完后, now指向最后一位的叶节点, 一般可以对其进行操作来着
    }
    int Ask(int x) {
        int now = root, ret = 0, tmp;
        for (int i = 31; i >= 0; i--) {
            tmp = (x >> i) & 1;
            if (!nxt[now][tmp ^ 1]) now = nxt[now][tmp];//如果不存在可以找的节点, now指向同值子节点
            else {
                now = nxt[now][tmp ^ 1];
                ret = ret | (1 << i);//用或运算更省时间
            }
        }
        return ret;
    }
}t[MAXN];
vector<int> son[MAXN];
void Merge(vector<int>& v1, vector<int>& v2, Trie& t1, Trie& t2) {  //合并字典树
    if(v2.size() > v1.size()) swap(v1, v2), swap(t1, t2);
    t2.Rec(t2.root);
    for (int i = 0; i < v2.size(); i++) {
        v1.push_back(v2[i]);
        t1.Insert(v2[i]);
    }
    v2.clear();
}
void DFS(int u) {
    son[u].clear(); son[u].push_back(val[u]);
    t[u].Init(); t[u].Insert(val[u]);
    for (int i = 0; i < E[u].size(); i++) {
        int v = E[u][i];
        DFS(v);
        Merge(son[u], son[v], t[u], t[v]);
    }
    //当前节点下的子树已经全部遍历完毕, 回溯以下答案
    for (int i = 0; i < ver[u].size(); i++) {
        ans[ver[u][i].second] = t[u].Ask(ver[u][i].first);
    }
}

int main() {
    int n, q, u, v, x;
    while(~scanf("%d%d", &n, &q)) {
        Init(n);                    //初始化
        for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
        for (int i = 2; i <= n; i++) {          
            scanf("%d", &v);
            E[v].push_back(i);             //将子节点存储进去
        }
        for (int i = 1; i <= q; i++) {          //读入u, x
            scanf("%d%d", &u, &x);
            ver[u].push_back(pair<int, int> (x, i));   //怎么理解这一步
        }
        DFS(1);
        for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
    }
    return 0;
}

思路:

构筑字典树上还有其他方法,如利用dfs序将树变成二维区间,接着再使用主席树的方法维护其字典树,根据询问的节点进行询问。

Code:

搬运自https://www.cnblogs.com/jianrenfang/p/7457308.html,自己的代码还没写,之后补上。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define met(a,b) memset(a,b,sizeof a)
#define pb push_back
#define mp make_pair
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 2e5+5;;
const int M = 17;
const int mod = 1e9+7;
const int mo=123;
const double pi= acos(-1.0);
typedef pair<int,int>pii;
 
int bin[31];
int n,m,tot;
int a[N],root[N],id[N],st[N],ed[N];
vector<int>edg[N];
struct trie{
    int cnt;
    int ch[N*32][2],sum[N*32];
    void init(){
        met(ch,0);met(sum,0);
        cnt=0;
    }
    int insert(int x,int val){
        int tmp,y;tmp=y=++cnt;
        for(int i=30;i>=0;i--)
        {
            ch[y][0]=ch[x][0];ch[y][1]=ch[x][1];
            sum[y]=sum[x]+1;
            int t=val&bin[i];t>>=i;
            x=ch[x][t];
            ch[y][t]=++cnt;
            y=ch[y][t];
        }
        sum[y]=sum[x]+1;
        return tmp;
    }
    int query(int l,int r,int val){
        int tmp=0;
        for(int i=30;i>=0;i--)
        {
            int t=val&bin[i];t>>=i;
            if(sum[ch[r][t^1]]-sum[ch[l][t^1]])
                tmp+=bin[i],r=ch[r][t^1],l=ch[l][t^1];
            else r=ch[r][t],l=ch[l][t];
        }
        return tmp;
    }
}trie;
void dfs(int u,int fa){
    st[u]=++tot;
    id[tot]=u;
    for(int i=0;i<edg[u].size();i++){
        int v=edg[u][i];
        if(v==fa)continue;
        dfs(v,u);
    }
    ed[u]=tot;
}
int main(){
    bin[0]=1;for(int i=1;i<=30;i++)bin[i]=bin[i-1]<<1;
    while(~scanf("%d%d",&n,&m)){
        root[0]=0;tot=0;
        trie.init();
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),edg[i].clear();
        for(int i=2;i<=n;i++){
            int v;
            scanf("%d",&v);
            edg[v].pb(i);
        }
        dfs(1,0);
        for(int i=1;i<=n;i++)root[i]=trie.insert(root[i-1],a[id[i]]);
        int l,r,x,u;
        while(m--){
            scanf("%d%d",&u,&x);
            l=st[u];r=ed[u];
            printf("%d\n",trie.query(root[l-1],root[r],x));
        }
    }
    return 0;
}

 

做题集——(字典树+启发式合并+dfs)Query on A Tree

原文:https://www.cnblogs.com/TanJI-life/p/14743674.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!