【GDOI2017模拟二试4.12】旅游路线(后缀自动机,trie)

Description

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君想知道共有多少种不同的旅行路线,相似的若干条旅行路线只算一种。

Solution

其实这题就是trie上SAM的裸题,直接反着连边从1开始走起就好了,因为只是双向边,诸神眷顾的幻想乡 还更难一点,是双向边。
但是空间问题怎么解决,用map就好了。

Code

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=100007;
int i,j,k,l,n,m;
long long ans;
int a[maxn*2],first[maxn*2],last1[maxn*2],next[maxn*2],tot; 
int last,num,np,nq,p,q,cc[maxn*2],cnt;
bool az[maxn];
map<int,int>son[maxn*40];
struct node{
    int fa,len;
}t[maxn*40];
void add(int x,int y){
    last1[++tot]=y;next[tot]=first[x];first[x]=tot;
}
int extend(int c,int last){
    np=++num,p=last;
    t[np].len=t[p].len+1;
    while(p&&!son[p][c])son[p][c]=np,p=t[p].fa;
    if(!p)t[np].fa=1;
    else{
        q=son[p][c];
        if(t[p].len+1==t[q].len)t[np].fa=q;
        else{
            t[nq=++num]=t[q];
            son[nq]=son[q];
            t[nq].len=t[p].len+1;
            t[q].fa=t[np].fa=nq;
            while(p&&son[p][c]==q)son[p][c]=nq,p=t[p].fa;
        }
    }
    last=np;
    return last;
}
void dfs(int x,int y,int z){
    int i,u=extend(a[x],z);
    az[x]=1;
    rep(i,x){
        if(last1[i]!=y){
            dfs(last1[i],x,u);    
        }
    }
}
int main(){
    freopen("route.in","r",stdin);
    freopen("route.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&k,&l);a[k]++;a[l]++;cc[l]++;
        add(l,k);
    }
    last=num=1;
    dfs(1,0,1);
    fo(i,2,num)ans+=t[i].len-t[t[i].fa].len;
    printf("%lld\n", ans);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值