HDU4601 Letter Tree

HDU4601 Letter Tree

提议:给出一颗树,根节点为1号节点,树边上有字母,从节点u往其子节点走m步,走出的字符串的哈希值最大为多少。

解题思路:将树分层,到1号节点深度相同的分到同一层。将原树构造成一颗trie树,并记录原树映射到trie树中的节点标号,可以在构造trie树时同时计算出1号节点到每个节点所构成的字符串的哈希值。构造好trie树后,可以按字典序遍历这颗trie树,并给每个节点按遍历的字典序标上rank,每个节点的rank必定是唯一的,再将rank为当前值得节点标号记录在下来(即记录rank的映射值),同时通过之前记录的原树的节点在trie树中的映射值,标出原树每个节点的rank值。找从u走m步能获得的最大哈希值,即找深度为u的深度加m后的那一层中,为u的子孙节点中的,从根到这些节点构成的字符串字典序最大的那个节点,也就是rank值最大的那个节点。我们遍历原树,那么在同一层中,属于同一祖先的那些节点遍历到的时间在层中,必定是在一起的。我们按划分好的层,将节点排列好,即第一层的节点都在最前面,接着第二层,接着。。排列好后,可以通过rmq,或者线段树计算某段区间的最值。那么我们只要找出深度为u的深度加m后的那一层中,为u的子孙的那些节点(在我们的排列好的数组中,必定是连续在一起的)的区间。询问该区间的最值就好了。求这段区间可以用二分,我们知道要查询的节点在第几层,那么就在这一层中,二分出这些节点的起始那个是在这一层中的第几个,终点是第几个,在排列序列时,我们同时也要将每一层的分界线记录下来,那么要查询的区间的l,r则分别是我们二分出来的起点+分界点,终点+分界点。如何二分出起点和终点呢,对于该层中,为u的子孙节点的时间戳必定大于u节点,小于u的下一个兄弟节点,根据这两个条件就可以分别二分出起点和终点。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define ll __int64
using namespace std ;

const int maxn = 211111 ;
int c[26][maxn] , tot , dis[maxn] , pos[maxn] , rk[maxn] ;
int tim[maxn] , dp[25][maxn] , cnt[maxn] , g[maxn] , line[maxn] , lon[maxn] ;
ll val[maxn] , f[25] , pow26[maxn] ;

const int mod = 1000000007 ;
struct Edge
{
    int to , next , v ;
} edge[maxn<<1] ;
int head[maxn] , E , n ;

struct Point
{
    int id ;
    int tim ;
    bool operator < ( const Point &t ) const
    {
        return tim < t.tim ;
    }
} ;
vector<Point> vec[maxn] ;

void new_edge ( int a , int b , char *s )
{
    edge[E].to = b ;
    edge[E].next = head[a] ;
    edge[E].v = s[0] - 'a' ;
    head[a] = E ++ ;
    edge[E].to = a ;
    edge[E].next = head[b] ;
    edge[E].v = s[0] - 'a' ;
    head[b] = E ++ ;
}

int new_node ()
{
    int i ;
    for ( i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ;
    val[tot] = 0 ;
    return tot ++ ;
}

int step ;
void cal ( int u , int fa , int now )
{
    int i ;
    Point fuck ;
    for ( i = head[u] ; i != -1 ; i = edge[i].next )
    {
        int v = edge[i].to ;
        if ( v == fa ) continue ;
        dis[v] = dis[u] + 1 ;
        tim[v] = ++ step ;
        fuck.id = v , fuck.tim = step ;
        vec[dis[v]].push_back ( fuck ) ;
        int k = edge[i].v ;
        if ( !c[k][now] ) 
        {
            c[k][now] = new_node () ;
            val[c[k][now]] = ( (ll) val[now] * 26 % mod + k ) % mod ;
        }
        pos[v] = c[k][now] ;
        cal ( v , u , c[k][now] ) ;
        lon[u] = max ( lon[u] , lon[v] ) ;
        cnt[u] += cnt[v] ;
    }
    lon[u] = max ( lon[u] , dis[u] ) ;
    cnt[u] ++ ;
}

void cal1 ( int now )
{
    rk[now] = step ++ ;
    g[rk[now]] = now ;
    int i ;
    for ( i = 0 ; i < 26 ; i ++ )
        if ( c[i][now] )
            cal1 ( c[i][now] ) ;
}

int dep ;

void init ()
{
    for ( int i = 0 ; i <= n ; i ++ )
    {
        head[i] = -1 , vec[i].clear () ;
        lon[i] = dis[i] = rk[i] = cnt[i] = 0 ;
    }
    tot = E = 0 ;
    new_node () ;
}

int T ;
void rmq ()
{
    int i , j ;
    T = 0 ;
    for ( i = 1 ; i <= n ; i ++ )
    {
        int l = vec[i].size () ;
        if ( l == 0 ) break ;
        line[i] = T + 1 ;
        for ( j = 0 ; j < l ; j ++ )
            dp[0][++T] = rk[pos[vec[i][j].id]] ;
    }
    for ( i = 1 ; f[i] <= T ; i ++ )
        for ( j = 1 ; j + f[i] - 1 <= T ; j ++ )
            dp[i][j] = max ( dp[i-1][j] , dp[i-1][j+f[i-1]] ) ;
}

int query ( int l , int r )
{
    if ( l == r ) return dp[0][l] ;
    if ( l > r ) swap ( l , r ) ;
    int i , j , k ;
    for ( i = 0 ; i <= 22 ; i ++ )
        if ( f[i] >= ( r - l + 1 ) )
        {
            k = i - 1 ;
            break ;
        }
    return max ( dp[k][l] , dp[k][r-f[k]+1] ) ;
}

char s[111] ;
int main ()
{
    int cas , i , j , a , b , q ;
    f[0] = pow26[0] = 1 ;
    for ( i = 1 ; i <= 22 ; i ++ )
        f[i] = (ll) f[i-1] * 2 % mod ;
    for ( i = 1 ; i < 100005 ; i ++ )
        pow26[i] = (ll) pow26[i-1] * 26 % mod ;
    scanf ( "%d" , &cas ) ;
    while ( cas -- )
    {
        scanf ( "%d" , &n ) ;
        init () ;
        for ( i = 1 ; i < n ; i ++ )
        {
            scanf ( "%d%d%s" , &a , &b , s ) ;
            new_edge ( a , b , s ) ;
        }
        step = 0 ;
        cal ( 1 , 0 , 0 ) ;
        step = 0 ;
        cal1 ( 0 ) ;
        rmq () ;
        pos[1] = 0 ;
        scanf ( "%d" , &q ) ;
        while ( q -- )
        {
            int u , m ;
            scanf ( "%d%d" , &u , &m ) ;
            if ( m == 0 )
            {
                puts ( "0" ) ;
                continue ;
            }
            if ( dis[u] + m > lon[u] )
            {
                puts ( "IMPOSSIBLE" ) ;
                continue ;
            }
            int dep = dis[u] + m ;
            Point fuck ;
            fuck.tim = tim[u] ;
            int l = lower_bound ( vec[dep].begin () , vec[dep].end () , fuck ) - vec[dep].begin () ;
            if ( l == vec[dep].size () ) l -- ;
            l += line[dep] ;
            fuck.tim = tim[u] + cnt[u] - 1 ;
            int r = upper_bound ( vec[dep].begin () , vec[dep].end () , fuck ) - vec[dep].begin () ;
            r -- ;
            r += line[dep] ;
            int k = query ( l , r ) ;
            k = g[k] ;
            u = pos[u] ;
            printf ( "%I64d\n" , ( val[k] - val[u] * pow26[m] % mod + mod ) % mod ) ;
        }
    }
}
/*
1000
25
1 2 d
2 3 h
3 4 z
4 5 z
5 6 l
6 7 t
7 8 w
8 9 q
9 10 q
10 11 h
11 12 e
12 13 a
13 14 a
14 15 d
15 16 r
16 17 a
17 18 g
18 19 f
19 20 g
20 21 c
21 22 c
22 23 b
23 24 m
24 25 l
1
20 2
8
1 2 a
1 3 b
2 4 c
2 5 c
4 8 a
5 7 d
3 6 f
6
7 1
7 0
3 2
3 1
2 1
2 2

9
1 2 c
1 3 c
2 4 a
2 8 b
2 6 c
3 7 d
3 5 e
3 9 f
5
1 2
1 1
2 1
3 1
3 2

9
1 2 a
2 3 b
3 4 c
4 5 d
5 6 e
6 7 f
7 8 g
8 9 h
10
1 1
1 2
1 3
1 4
1 5
1 6 
1 7
1 8
2 1
2 2



你可能感兴趣的:(算法)