HDU 4601 Letter Tree (线段树+字典树+树型转线性)

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

题目:给出一棵树,1为根,边为字母,问从某个结点,向下走m步,路径形成一个字符串,要求字典序最大。

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

做法:直接做根本没法。。。每个结点孩子结点边上的字母可能相等。直接贪心都没法。m也很大。

我们如果知道从根到每个结点路径形成的字符串,

那么固定u,m,查询的话,终点必定是在同一层,depth[u] + m。从根到这个结点的字符串长度是一样的,而且必定存在LCP >= depth[u]。所以我们可以直接比较从根到终点的路径的字符串,而且对于每次查询,终点是在同一层。

我们就可以萌生离线做法,对于终点在同一层的查询一并处理。

那么我们需要求出根到每个结点的字典序排名,以及HASH值。

然后对于这样树型转线性的应该 不陌生,利用DFS的时间戳。

然后 对于每一层,可以用RMQ或者线段树查询最值。注意最值是字典序,不要用HASH值的大小。

接下来的问题是怎么得到字典序,原因在于原来的树每个结点的孩子中有相同的字母,那么我们就重构Trie。

大概就可以解决了:

step 1:构造Trie,避免边上字母一样的情况,作一些合并,原来的结点映射到Trie上的结点。

step 2:遍历Trie,给原来的结点标上rank值。

step 3:遍历原来的树,得到时间戳。

step 4:将查询分类之后,处理每一层,将同一层的值放到RMQ或者线段树中。

step 5:每个查询,二分得到对应区间,区间查询。

注意 :数据中有m = 0 的情况,坑啊。。。。。。。。。。。。。。。输出0

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define lson step << 1
#define rson step << 1 | 1
#pragma comment(linker,"/STACK:100000000,100000000")
using namespace std;
typedef long long LL;
const int N = 200005;
const LL MOD = 1000000007LL;
const LL HASH = 26LL;
struct Edge {
    int v , next , ch ;
}e[N << 1];
struct Trie {
    Trie *next[26];
    vector <int> v;
    void init () {
        v.clear();
        memset (next , 0 , sizeof(next));
    }
}s[N];
struct Question {
    int u , d , id;
    void input (int _i) {
        scanf ("%d %d" , &u , &d);
        id = _i;
    }
}question[N];
struct Seg_tree {
    int left , right;
    int maxrank;
    LL hash;
}L[N << 2];
vector<Question>askondep[N];
int n , start[N] , tot , idx;
LL hash[N] , fac[N] = {1};
int ans[N];
void _add (int u , int v , int c) {
    e[tot].v = v;
    e[tot].ch = c;
    e[tot].next = start[u];
    start[u] = tot ++;
}
void add (int u , int v , int c) {
    _add (u , v , c);
    _add (v , u , c);
}
Trie *NewNode () {
    Trie *t = &s[idx ++];
    t->init();
    return t;
}
void bulidtrie (Trie *p , int u , int pre , LL h) {
    p -> v.push_back (u);
    hash[u] = h;
    for (int i = start[u] ; i != -1 ; i = e[i].next) {
        int v = e[i].v , ch = e[i].ch;
        if (v == pre) continue;
        if (p -> next[ch] == NULL) p -> next[ch] = NewNode ();
        bulidtrie (p -> next[ch] , v , u , (h * HASH + ch) % MOD);
    }
}
int nowrank , rank[N];
void getrank (Trie *p ) {
    for (int i = 0 ; i < p -> v.size() ; i ++) {
        rank[p -> v[i]] = nowrank;
    }
    for (int i = 0 ; i < 26 ; i ++) {
        if (p -> next[i] != NULL) {
            nowrank ++;
            getrank (p -> next[i]);
        }
    }
}
vector<int> ondepth[N];
int depth[N] , id[N] , left_id[N] , right_id[N] , id_cnt , maxdep , mapping[N];
void caldepth (int u , int pre , int dep) {
    depth[u] = dep;
    id[u] = ++id_cnt;
    mapping[id_cnt] = u;
    left_id[u] = id_cnt + 1;
    ondepth[dep].push_back (id_cnt);
    maxdep = max (maxdep , dep);
    for (int i = start[u] ; i != -1 ; i = e[i].next) {
        int v = e[i].v;
        if (v == pre) continue;
        caldepth (v , u , dep + 1);
    }
    right_id[u] = id_cnt;
}
void push_up (int step) {
    if (L[lson].maxrank > L[rson].maxrank) {
        L[step].maxrank = L[lson].maxrank;
        L[step].hash = L[lson].hash;
    }
    else {
        L[step].maxrank = L[rson].maxrank;
        L[step].hash = L[rson].hash;
    }
}
void bulid (int step , int l , int r , int d) {
    L[step].left = l;
    L[step].right = r;
    if (l == r) {
        L[step].maxrank = rank[mapping[ondepth[d][l]]];
        L[step].hash = hash[mapping[ondepth[d][l]]];
        return ;
    }
    int m = (l + r) >> 1;
    bulid (lson , l , m , d);
    bulid (rson , m + 1 , r , d);
    push_up (step);
}
pair<int , LL> query (int step , int l , int r) {
    if (l < L[step].left || r > L[step].right || l > r) return make_pair(-1 , -1);
    if (L[step].left == l && L[step].right == r) {
        return make_pair (L[step].maxrank , L[step].hash);
    }
    int m = (L[step].left + L[step].right) >> 1;
    if (r <= m) return query (lson , l , r);
    else if (l > m) return query (rson , l , r);
    else {
        pair<int , int> u = query (lson , l , m);
        pair<int , int> v = query (rson , m + 1 , r);
        if (u.first > v.first) return u;
        return  v;
    }
}
int main () {
    #ifndef ONLINE_JUDGE
        freopen ("input.txt" , "r" , stdin);
        freopen ("output.txt" , "w" , stdout);
    #endif
    for (int i = 1 ; i < N ; i ++)
        fac[i] = (fac[i - 1] * 26LL) % MOD;
    int t , out = 0;
    scanf ("%d" , &t);
    while (t --) {
        tot = 0;idx = 0;
        memset (start , -1 , sizeof(start));
        scanf ("%d" , &n);
        for (int i = 1 ; i < n ; i ++) {
            int u , v ; char str[5];
            scanf ("%d %d %s" , &u , &v , str);
            add (u , v , str[0] - 'a');
        }
        Trie *root = NewNode ();
        bulidtrie (root , 1 , -1 , 0LL);
        nowrank = 0;
        getrank (root);
        id_cnt = 0;maxdep = 1;
        caldepth (1 , -1 , 1);
        int q;
        scanf ("%d" , &q);
        for (int i = 0 ; i < q ; i ++) {
            question[i].input(i);
            if (question[i].d == 0) ans[i] = 0;
            else if (depth[question[i].u] + question[i].d <= maxdep)
                askondep[depth[question[i].u] + question[i].d].push_back (question[i]);
            else  ans[i] = -1;
        }
        for (int dep = 1 ; dep <= maxdep ; dep ++) {
            if (askondep[dep].size () == 0) continue;
            int m = ondepth[dep].size() - 1;
            bulid (1 , 0 , m , dep);
            for (int i = 0 ; i < askondep[dep].size() ; i ++) {
                int u = askondep[dep][i].u , d = askondep[dep][i].d , id = askondep[dep][i].id;
                int l = left_id[u] , r = right_id[u];
                l = lower_bound (ondepth[dep].begin () , ondepth[dep].end () , l) - ondepth[dep].begin ();
                r = upper_bound (ondepth[dep].begin () , ondepth[dep].end () , r) - ondepth[dep].begin () - 1;   
                pair<int , LL> ret = query (1 , l , r);
                if (ret.second == -1) ans[id] = -1;
                else ans[id] = ((ret.second - (LL)hash[u] * fac[d]) % MOD + MOD) % MOD;
            }
        }
        for (int i = 0 ; i < q ; i ++) {
            if (ans[i] == -1) puts ("IMPOSSIBLE");
            else printf("%d\n" , ans[i]);
        }
        for (int i = 1 ; i <= maxdep ; i ++) {
            ondepth[i].clear();
            askondep[i].clear();
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值