Description
幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日。
Input
第一行两个正整数n,c。表示空地数量和颜色数量。
Output
一行,输出一个整数,表示答案。
Sample Input
0 2 1 2 1 0 0
1 2
3 4
3 5
4 6
5 7
2 5
Sample Output
HINT
对于所有数据,1<=n<=100000, 1<=c<=10。
题解:由于叶子数量只有20个,所以可以枚举叶子,以每个叶子为根,这样我们就相当于得到了一堆Trie树。
那么广义后缀自动机,就是在多个串,或者Trie树上建的后缀自动机。与普通SAM的区别就是,每次的last指针指向的是它在Trie树上的父亲 在后缀自动机上对应的节点。不过呢,如果你像普通SAM一样来建广义SAM,可能会多建出一些没用的点,不过由于本人懒,多建一些点就多建吧,反正影响不大。
然后这里问的是不同的子串的个数,答案就是每个点的mx-它的parent的mx。怎么理解呢?我们将parent树拎出来,每个点都可以当做一些子串的结束节点,在每个节点结束的子串数量就是mx,但是这样肯定有重复的,我们要减去之前已经被计算过的子串个数,也就是减去parent的mx就行了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxm=2000010; const int maxn=200010; int n,tot,cnt,last; int ch[maxm][10],mx[maxm],pre[maxm]; int to[maxn],next[maxn],head[maxn],v[maxn],d[maxn]; long long ans; int extend(int p,int x) { if(ch[p][x]&&mx[ch[p][x]]==mx[p]+1) return ch[p][x]; //这里可以防止多建点 int np=++tot; mx[np]=mx[p]+1; for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np; if(!p) pre[np]=1; else { int q=ch[p][x]; if(mx[q]==mx[p]+1) pre[np]=q; else { int nq=++tot; mx[nq]=mx[p]+1,pre[nq]=pre[q],pre[np]=pre[q]=nq; memcpy(ch[nq],ch[q],sizeof(ch[q])); for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq; } } return np; } void dfs(int x,int fa,int p) { int i,np=extend(p,v[x]); for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa) dfs(to[i],x,np); } void add(int a,int b) { to[cnt]=b,next[cnt]=head[a],head[a]=cnt++; } int main() { int i,a,b; scanf("%d%d",&n,&a); for(i=1;i<=n;i++) scanf("%d",&v[i]); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) { scanf("%d%d",&a,&b); add(a,b),add(b,a); d[a]++,d[b]++; } tot=1; for(i=1;i<=n;i++) if(d[i]==1) dfs(i,0,1); for(i=1;i<=tot;i++) ans+=mx[i]-mx[pre[i]]; printf("%lld",ans); return 0; }
| 欢迎来原网站坐坐! >原文链接<