5052. 旅游路线

题目大意

给定一个 n 个点的tire,求有多少个不同的子串。

Data Constraint
n105

题解

如果将tire上所有的串取出来,做一遍SA,显然就可以很方便的计算出答案了。
所以考虑如何对tire做SA。
还是用倍增的方法,只是原来对一个串的倍增变成了树上的倍增。
求Height也是用倍增的方法求。

时间复杂度: O(nlogn)
我程序里偷了一下懒,没用基数排序多了一个 log

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std ;

#define N 100000 + 10
typedef long long ll ;
const int MAXN = 17 ;

int f[N][MAXN] , Rank[N][MAXN] , SA[N] ;
int Node[N] , Next[N] , Head[N] , tot ;
int Deg[N] , Deep[N] ;
int n , lasd ;
ll ans ;

void link( int u , int v ) {
    Node[++tot] = v ;
    Next[tot] = Head[u] ;
    Head[u] = tot ;
}

void DFS( int x ) {
    SA[x] = x ;
    for (int p = Head[x] ; p ; p = Next[p] ) {
        f[Node[p]][0] = x ;
        Deep[Node[p]] = Deep[x] + 1 ;
        Rank[Node[p]][0] = Deg[Node[p]] ;
        DFS( Node[p] ) ;
    }
}

bool cmp( int a , int b ) {
    return Rank[a][lasd] < Rank[b][lasd] || ( Rank[a][lasd] == Rank[b][lasd] && Rank[f[a][lasd]][lasd] < Rank[f[b][lasd]][lasd] ) ;
}

bool Equal( int x , int y ) { return Rank[x][lasd] == Rank[y][lasd] && Rank[f[x][lasd]][lasd] == Rank[f[y][lasd]][lasd] ; }

int main() {
    freopen( "route.in" , "r" , stdin ) ;
    freopen( "route.out" , "w" , stdout ) ;
    scanf( "%d" , &n ) ;
    for (int i = 1 ; i < n ; i ++ ) {
        int u , v ;
        scanf( "%d%d" , &u , &v ) ;
        link( v , u ) ;
        Deg[u] ++ , Deg[v] ++ ;
    }
    Rank[1][0] = Deg[1] ;
    Deep[1] = 1 ;
    DFS( 1 ) ;
    for (int j = 1 ; j < MAXN ; j ++ ) {
        for (int i = 1 ; i <= n ; i ++ ) {
            f[i][j] = f[f[i][j-1]][j-1] ;
        }
    }
    for (int d = 1 ; d < MAXN ; d ++ ) {
        lasd = d - 1 ;
        sort( SA + 1 , SA + n + 1 , cmp ) ;
        int p = 1 ;
        Rank[SA[1]][d] = 1 ;
        for (int i = 2 ; i <= n ; i ++ ) {
            if ( !Equal( SA[i-1] , SA[i] ) ) p ++ ;
            Rank[SA[i]][d] = p ;
        }
    }
    for (int i = 1 ; i <= n ; i ++ ) {
        ans += Deep[SA[i]] ;
        int Height = 0 ;
        int x = SA[i-1] , y = SA[i] ;
        for (int j = MAXN - 1 ; j >= 0 ; j -- ) {
            if ( Deep[x] < (1 << j) || Deep[y] < (1 << j) ) continue ;
            if ( Rank[x][j] == Rank[y][j] ) {
                x = f[x][j] , y = f[y][j] ;
                Height += (1 << j) ;
            }
        }
        ans -= Height ;
    }
    printf( "%lld\n" , ans ) ;
    return 0 ;
}

以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值