1. 程式人生 > >HDU 4757 Tree (倍增演算法求LCA + 可持久化Trie樹)

HDU 4757 Tree (倍增演算法求LCA + 可持久化Trie樹)

題目大意:

就是現在給出一棵樹, 結點個數不超過10W, 每個節點上有一個不超過2^16的非負整數, 然後10W次詢問, 每次詢問兩個節點的路徑上的所有數中異或上給出的數的最大值

大致思路:

剛開始做這個題想的是樹鏈剖分之後用線段樹套Trie樹來做...結果悲劇地MLE了...

另外那樣做得話複雜度其實是比較大的...每次詢問都是16*(logn)*logn級別的..

後來發現是個可持久化Trie樹...

表示第一次寫可持久化Trie樹, 感覺和主席樹很像, 都是多個版本的Trie的集合體, 然後由要訪問的版本的不同而從不同的根節點出發來訪問

對於給出的樹按照父親和兒子的關係來建立可持久化Trie樹

每次訪問的時候訪問到的Trie樹版本是考慮了從當前結點到父親結點的路徑上的所有點的一顆Trie樹, 那麼對於每次的路徑(u, v)的查詢, 就是u開始的Trie與v開始的Trie的查詢減去他們的LCA的查詢, 然後利用貪心的思想求出可能的最大值即可

程式碼如下:

Result  :  Accepted     Memory  :  41812 KB     Time  :  1528 ms

/*
 * Author: Gatevin
 * Created Time:  2015/10/2 15:20:38
 * File Name: Sakura_Chiyo.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 100010
struct Edge
{
    int u, v, nex;
    Edge(int _u, int _v, int _nex)
    {
        u = _u, v = _v, nex = _nex;
    }
    Edge(){}
};
Edge edge[maxn << 1];
int head[maxn];
int tot;

void add_Edge(int u, int v)
{
    edge[++tot] = Edge(u, v, head[u]);
    head[u] = tot;
}

int w[maxn];
int father[maxn][20];
int dep[maxn];
int n, m;

/*
 * 可持久化Trie樹
 */
struct Trie
{
#define maxnode 2800100
    int next[maxnode][2];
    int L;
    int root[maxn];
    int sz[maxnode];
    void init()
    {
        L = 0;
        memset(root, 0, sizeof(root));
        memset(sz, 0, sizeof(sz));
    }
    int newnode()
    {
        next[L][0] = next[L][1] = -1;
        L++;
        return L - 1;
    }
    
    /*
     * 一個以root[x]開始的Trie樹是樹上x向上到鏈的陣列成的Trie樹
     * 然後樹上每個節點都用sz[u][v]表示以u向上到根的值組成的Trie樹中結點v代表的出現次數
     */
    void insert(int x, int fa, int value)//x的父親節點是y
    {
        x = root[x];
        int y = root[fa];
        for(int i = 15; i >= 0; i--)
        {
            int nex = 0;
            if(value & (1 << i)) nex = 1;
            if(next[x][nex] == -1)
            {
                int id = newnode();
                next[x][nex] = id;
                next[x][nex^1] = next[y][nex^1];//合併
                sz[next[x][nex]] = sz[next[y][nex]];
            }
            x = next[x][nex], y = next[y][nex];
            ++sz[x];
        }
    }
    
    int query(int x, int y, int z, int value)//z是x和y的lca
    {
        int ret = 0, ano = w[z];
        x = root[x], y = root[y], z = root[z];
        for(int i = 15; i >= 0; i--)
        {
            int nex = 0;
            if(value & (1 << i)) nex = 1;
            if(sz[next[x][nex ^ 1]] + sz[next[y][nex ^ 1]] - 2*sz[next[z][nex ^ 1]] > 0)//說明在[x, y]的鏈上有數提供這樣的路走
            {
                x = next[x][nex ^ 1], y = next[y][nex ^ 1], z = next[z][nex ^ 1];
                ret += (1 << i);
            }
            else
            {
                x = next[x][nex], y = next[y][nex], z = next[z][nex];
            }   
        }
        return max(ret, ano ^ value);
    }
};

Trie trie;

void dfs(int now)
{
    trie.root[now] = trie.newnode();
    trie.insert(now, father[now][0], w[now]);//按照樹的列的順序插入
    for(int i = head[now]; i + 1; i = edge[i].nex)
    {
        if(edge[i].v == father[now][0]) continue;
        father[edge[i].v][0] = now;
        dep[edge[i].v] = dep[now] + 1;
        
        dfs(edge[i].v);
    }
    return;
}

int swim(int x, int H)
{
    for(int i = 0; H > 0; i++)
    {
        if(H & 1) x = father[x][i];
        H >>= 1;
    }
    return x;
}

int lca(int x, int y)
{
    int i;
    if(dep[x] < dep[y]) swap(x, y);
    int k = dep[x] - dep[y];
    x = swim(x, k);
    if(x == y) return x;
    while(1)
    {
        for(i = 0; father[x][i] != father[y][i]; i++);
        if(i == 0) return father[x][0];
        x = father[x][i - 1];
        y = father[y][i - 1];
    }
    return -1;
}

void answer(int x, int y, int z)
{
    printf("%d\n", trie.query(x, y, lca(x, y), z));
}

bool get(int& x)
{
    x = 0;
    char c;
    bool ined = 0;
    while(!ined)
    {
        c = getchar();
        if(c == EOF) break;
        while(c >= '0' && c <= '9')
        {
            ined = 1;
            x = (x << 3) + (x << 1) + c - '0';
            c = getchar();
        }
    }
    return ined;
}


int main()
{
    while(get(n))
    {
        get(m);
        memset(head, -1, sizeof(head));
        tot = 0;
        for(int i = 1; i <= n; i++)
            get(w[i]);
        int u, v;
        for(int i = 1; i < n; i++)
        {
            get(u); get(v);
            add_Edge(u, v);
            add_Edge(v, u);
        }
        
        trie.init();
        memset(father, 0, sizeof(father));
        dep[1] = 1;
        father[1][0] = -1;
        dfs(1);
        for(int j = 1; (1 << j) <= n; j++)
            for(int i = 1; i <= n; i++)
                if(father[i][j - 1] != -1)
                    father[i][j] = father[father[i][j - 1]][j - 1];//準備好求lca
        
        while(m--)
        {
            int x, y, z;
            scanf("%d %d %d", &x, &y, &z);
            answer(x, y, z);
        }
    }
    return 0;
}