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