BJ模拟:Different Trips(树上后缀数组)

这篇博客介绍了如何在树上应用后缀数组进行BJ模拟,重点在于第二关键字的计数排序。

传送门

题解:
后缀数组。

树上唯一的不同是第二关键字要先计数排序。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

const int N=5e5+50, L=18;
const ULL base=31;
int n, fa[N][L+2], bin[L+2], dep[N], h[N]; 
ULL pw[N*2], g[N][L+2];
int sa[N*2], *rk=new int[N*2](), *rk2=new int[N*2]();
vector <int> edge[N];

inline void dfs(int x,int f) {
    fa[x][0]=f; g[x][0]=edge[x].size(); rk[x]=g[x][0]; dep[x]=dep[f]+1;
    for(int i=1;i<=L;i++) {
        fa[x][i]=fa[fa[x][i-1]][i-1];
        g[x][i]=g[x][i-1]*pw[bin[i-1]]+g[fa[x][i-1]][i-1];
    }
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; if(v==f) continue;
        dfs(v,x); 
    }
}
inline void Rsort() {
    static int c[N], q[N];
    for(int i=1;i<=n;i++) c[i]=0;
    for(int i=1;i<=n;i++) c[rk2[i]]++;
    for(int i=1;i<=n;i++) c[i]+=c[i-1];
    for(int i=1;i<=n;i++) q[c[rk2[i]]--]=i;

    for(int i=1;i<=n;i++) c[i]=0;
    for(int i=1;i<=n;i++) c[rk[i]]++;
    for(int i=1;i<=n;i++) c[i]+=c[i-1];
    for(int i=n;i>=1;i--) sa[c[rk[q[i]]]--]=q[i]; 
}
inline int lcp(int x,int y) {
    int xx=x, yy=y;
    for(int i=L;i>=0;i--)
        if(g[xx][i]==g[yy][i])
            xx=fa[xx][i], yy=fa[yy][i];
    return min(dep[x]-dep[xx],dep[y]-dep[yy]);
}
inline void build_sa() {
    for(int i=0,q;i<=L;++i) {
        for(int j=1;j<=n;j++) rk2[j]=rk[fa[j][i]];
        Rsort(); swap(rk,rk2); rk[sa[1]]=q=1;
        for(int j=2;j<=n;j++) 
            rk[sa[j]]=(rk2[sa[j]]==rk2[sa[j-1]] && rk2[fa[sa[j]][i]]==rk2[fa[sa[j-1]][i]]) ? q : ++q;
    }
    for(int i=2;i<=n;i++) 
        h[i]=lcp(sa[i-1],sa[i]);
}
int main() {
    n=rd(); pw[0]=1;
    for(int i=0;i<=L;i++) bin[i]=1<<i;
    for(int i=1;i<N*2;++i) pw[i]=pw[i-1]*base;
    for(int i=1;i<n;i++) {
        int x=rd(), y=rd();
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    dfs(1,0);
    build_sa();
    long long ans=0;
    for(int i=1;i<=n;i++) 
        ans+=dep[sa[i]]-h[i];
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值