【ZJOI2015】诸神眷顾的幻想乡

此生无悔入东方,来世愿生幻想乡。

Description

(⊙v⊙)嗯,既然是我大东方的题,那么还是贴原题吧。。
这里写图片描述
这里写图片描述

Solution

对于题面最下面那一行,你看完之后的第一反应是什么?
陈老师语文真好~
反正我是被坑了。。
生无可恋脸.jpg
不是度数<=20,而是叶子个数<=20!!!
那么我们发现,对于每个叶子我们都可以以它为根然后建一棵trie树。
那么我们的树上的子串就变成了trie树中从上到下的一段了。
然后我们把所有的trie合并成一个大trie。
题目就相当于要求大trie的本质不同的子串个数。
你可以把每一个路径拉下来,两两中间插入特殊字符,然后用SA做。
常数。。。
于是我们可以用SAM做。
trie树上的SAM?每个点的last就是它的父亲嘛。。
然后答案就是不同的状态和它的parent之间的距离和。
显然这些状态都是存在1个不一样的子串的。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 100005
using namespace std;
typedef long long ll;
struct note{int son[11],pr,len;}sam[N*20];
int n,m,x,y,l,tot,r[N],c[N];
int t[N*2],next[N*2],last[N];
void add(int x,int y) {
    t[++l]=y;next[l]=last[x];last[x]=l;
}
int extend(int x,int p) {
    int np=++tot;sam[np].len=sam[p].len+1;
    while (p&&!sam[p].son[x]) sam[p].son[x]=np,p=sam[p].pr;
    if (!p) sam[np].pr=1;else {
        int q=sam[p].son[x];
        if (sam[q].len==sam[p].len+1) sam[np].pr=q;
        else {
            int nq=++tot;
            sam[nq]=sam[q];
            sam[nq].len=sam[p].len+1;
            sam[q].pr=sam[np].pr=nq;
            while (p&&sam[p].son[x]==q) sam[p].son[x]=nq,p=sam[p].pr;
        }
    }
    return np;
}
void dfs(int x,int y,int l) {
    int la=extend(c[x],l);
    rep(i,x) if (t[i]!=y) dfs(t[i],x,la);
}
int main() {
    scanf("%d%d",&n,&m);tot=1;
    fo(i,1,n) scanf("%d",&c[i]);
    fo(i,1,n-1) {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
        r[x]++;r[y]++;
    }
    fo(i,1,n) if (r[i]==1) dfs(i,0,1);ll ans=0;
    fo(i,1,tot) ans+=sam[i].len-sam[sam[i].pr].len;
    printf("%lld\n",ans);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值