HDU 4757 Tree(可持续化字典树,lca)

题意:询问树上结点x到结点y路上上的权值异或z的最大值。

任意结点权值 ≤ 2^16,可以想到用字典树。

但是因为是询问某条路径上的字典树,将字典树可持续化,字典树上的结点保存在这条路径上的二进制数。

按照dfs序建树,结点u的字典树表示u到根结点路径上的字典树。

如果两个结点u和v,在同一条通往根结点的路径上,将会满足可减性。

因此只需要知道u、v、lca和fa[lca]四个结点的字典树就可以回答了。

/*********************************************************
*            ------------------                          *
*   author AbyssFish                                     *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<numeric>
#include<climits>
using namespace std;

const int maxn = 1e5+5;
const int maxd = 16;
const int maxnds = (maxd+1)*maxn;


int hd[maxn], nx[maxn<<1], to[maxn<<1], ec;
void add_edge(int u,int v)
{
    nx[ec] = hd[u];
    to[ec] = v;
    hd[u] = ec++;
}
void init_g(int n){ memset(hd+1,0xff,n*sizeof(int)); ec = 0; }
#define eachedge int i = hd[u]; ~i; i = nx[i]
#define ifvalid int v = to[i]; if(v == fa[u]) continue;

struct Node
{
    int ch[2];
    int v;
}p[maxnds];
int tot;

int n,m;
int stk[maxd+5];

inline int *dcmp(int x)
{
    for(int i = 0; i < maxd; i++){
        stk[i] = x>>i&1;
    }
    return stk;
}

void build(int *o, int v)
{
    int *s = dcmp(v);
    for(int i = maxd-1; i >= 0; i--){
        o = &(p[*o].ch[s[i]]);
        p[++tot] = p[*o];
        *o = tot;
        p[*o].v++;
    }
}

int a[maxn];
int fa[maxn];
int root[maxn];

void dfs_build(int u,int f = 0)
{
    p[root[u] = ++tot] = p[root[fa[u] = f]];
    build(root+u,a[u]);
    for(eachedge){
        ifvalid
        dfs_build(v,u);
    }

}

int *c_cmp;
bool cmp_id(int i,int j){ return c_cmp[i] < c_cmp[j]; }

int dep[maxn];
int path[maxn<<1];
int pid[maxn];
int dfs_clk;


void get_path(int u,int d)
{
    dep[u] = d;
    path[++dfs_clk] = u;
    pid[u] = dfs_clk;
    for(eachedge){
        ifvalid
        get_path(v,d+1);
        path[++dfs_clk] = u;
    }
}

struct SparseTable
{
    int mxk[maxn<<1];
    int d[maxn<<1][18];
    void init(int *mp,int *r, int n)
    {
        mxk[0] = -1;
        for(int i = 1; i <= n; i++){
            d[i][0] = r[i];
            mxk[i] = ((i&(i-1)) == 0) ?mxk[i-1]+1:mxk[i-1];
        }
        c_cmp = mp;
        for(int j = 1; j <= mxk[n]; j++){
            int t = (1<<j)-1, s = 1<<(j-1);
            for(int i = 1; i + t <= n; i++){
                d[i][j] = min(d[i][j-1],d[i+s][j-1],cmp_id);
            }
        }


    }
    int RMQ(int l,int r)
    {
        int k = mxk[r-l];
        return min(d[l][k],d[r-(1<<k)][k],cmp_id);
    }
}rmq;

void lca_init(int u)
{
    dfs_clk = 0;
    get_path(u,1);
    rmq.init(dep,path,dfs_clk);
}

int q_lca(int u, int v)
{
    if(pid[u] > pid[v]) swap(u,v);
    return rmq.RMQ(pid[u],pid[v]+1);
}

vector<int> Tree;

int cal_ch(int d)
{
    int re = 0;
    for(int i = 0; i < (int)Tree.size(); i++){
        int o = Tree[i];
        re += o >= 0? p[p[o].ch[d]].v : -p[p[-o].ch[d]].v;
    }
    return re;
}

void dump(int d)
{
    for(int i = 0; i < (int)Tree.size(); i++){
        int &o = Tree[i];
        o = o >= 0? p[o].ch[d] : -p[-o].ch[d];
    }
}

int query(int x,int y,int z)
{
    int re = z&~((1<<16)-1);
    int *s = dcmp(z);
    Tree.clear();
    Tree.push_back(root[x]);
    Tree.push_back(root[y]);
    int lca = q_lca(x,y);
    Tree.push_back(-root[lca]);
    Tree.push_back(-root[fa[lca]]);

    int tmp = 0;
    for(int i = maxd-1; i >= 0; i--, tmp <<= 1){
        int d = s[i]^1;
        if(cal_ch(d)){
            tmp |= 1;
            dump(d);
        }
        else dump(d^1);
    }
    return re|(tmp>>1);
}

void solve()
{
    for(int i = 1; i <= n; i++){
        scanf("%d",a+i);
    }
    init_g(n);
    for(int i = 1,u,v; i < n; i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    tot = 0;
    dfs_build(1);
    lca_init(1);
    for(int i = 0, x, y, z; i < m; i++){
        scanf("%d%d%d",&x,&y,&z);
        printf("%d\n",query(x,y,z));
    }
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    //p[0] = {{0,0},0}; //root[0] = 0;
    while(~scanf("%d%d",&n,&m)){
        solve();
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/jerryRey/p/5042024.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值