旅游路线

题目描述

A君准备在Z国进行一次旅行,Z国中有n个城市,城市从1到n进行编号,其中1号城市为Z国首都。Z国的旅行交通网由n-1条单向道路构成,并且从任何一个城市出发都可以通过旅行网到达首都。
一条旅行交通网中的旅行线路,可以用线路上所经过的城市来描述,如{v1,v2,v3,……,vm},它表示一条经过了m个城市的旅行路线,且城市vi到城市vi+1有一条单向道路相连。
两个城市是相似的,当且仅当他们所连接的道路数相同。
若两条路线{u1,u2,……,up}与{v1,v2,……,vq},若p=q且∀1 ≤ i ≤ p,城市 u i 与 v i 是相似的,则 A君认为这两条旅行路线也是相似的。
现在A君想知道共有多少种不同的旅行路线,相似的若干条旅行路线只算一种。

trie上建sam

trie上建广义sam裸题

#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
map<int,int> g[maxn*2];
map<int,int>::iterator it;
ll size[maxn*2];
int fail[maxn*2],step[maxn*2],b[maxn],a[maxn*2];
int h[maxn],go[maxn*2],next[maxn*2],d[maxn],dl[maxn],sum[maxn*2];
int i,j,k,l,t,n,m,tot,top,ans,last,head,tail,now;
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void ins(int c){
    int p=last;
    if (g[p][c]){
        int q=g[p][c];
        if (step[q]==step[p]+1) last=q;
        else{
            int i,nq=++tot;
            size[nq]=1;
            step[nq]=step[p]+1;
            g[nq]=g[q];
            fail[nq]=fail[q];
            fail[q]=nq;
            while (p&&g[p][c]==q){
                g[p][c]=nq;
                p=fail[p];
            }
            last=nq;
        }
        return;
    }
    int np=++tot;
    size[np]=1;
    step[np]=step[p]+1;
    while (p&&g[p][c]==0){
        g[p][c]=np;
        p=fail[p];
    }
    if (!p) fail[np]=1;
    else{
        int q=g[p][c];
        if (step[q]==step[p]+1) fail[np]=q;
        else{
            int nq=++tot;
            size[nq]=1;
            step[nq]=step[p]+1;
            g[nq]=g[q];
            fail[nq]=fail[q];
            fail[q]=nq;
            fail[np]=nq;
            while (p&&g[p][c]==q){
                g[p][c]=nq;
                p=fail[p];
            }
        }
    }
    last=np;
}
int main(){
    freopen("route.in","r",stdin);freopen("route.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        d[j]++;d[k]++;
        add(k,j);
    }
    tot=1;
    dl[tail=1]=1;
    last=1;
    ins(d[1]);
    b[1]=last;
    while (head<tail){
        now=dl[++head];
        t=h[now];
        while (t){
            last=b[now];
            ins(d[go[t]]);
            b[go[t]]=last;
            dl[++tail]=go[t];
            t=next[t];
        }
    }
    fo(i,1,tot) sum[step[i]]++;
    fo(i,1,tot) sum[i]+=sum[i-1];
    fd(i,tot,1) a[sum[step[i]]--]=i;
    fd(i,tot,1){
        t=a[i];
        it=g[t].begin();
        while (it!=g[t].end()){
            j=(*it).second;
            size[t]+=size[j];
            it++;
        }
    }
    printf("%lld\n",size[1]);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值