您的位置:首页 > 其它

[BZOJ3926]ZJOI2015诸神眷顾的幻想乡|后缀自动机

2015-06-20 22:19 357 查看
  注意到非常关键的条件,只与一个空地相邻的空地数量不超过20个,也就是叶子不超过20个,这意味着啥?考虑u到v的路径,一定存在某个叶子,当以这个叶子为根的时候u是v的祖先,也就是说所有的序列都是某个叶子为根的树的一条直链,把一棵树看出一个trie那么我们要做的就是统计这最多20个trie拼成的大trie的不重复子串数量啦!陈老师是厉害呀…

  窝只会SAM做法。。按大trie建出SAM,然后可以像3998那样统计。。或者考虑一个子串,它一定是由它的parent对应的串在前面加上一些字符得到,可以加几个呢?那不就是l[i]−l[fa[i]]l[i]-l[fa[i]]嘛。。那么当到fa[i]的子串计算完之后走到i只要加上上面那个东西就好了,所以答案就是∑节点数i=1(l[i]−l[fa[i]])\sum_{i=1}^{节点数}(l[i]-l[fa[i]])。。

#include<cstdio>
#include<iostream>
#include<memory.h>
#define N 4000005
using namespace std;
struct edge{
int e,next;
}ed[200005];
int n,c,s,e,ne=0,nd=1,i,a
[10],col[100005],l
,fa
,du[100005],h[100005],u
;
long long ans,f
;
void add(int s,int e)
{
ed[++ne].e=e;ed[ne].next=h[s];h[s]=ne;
}
int extend(int p,int c)
{
int np=++nd;
l[np]=l[p]+1;
while (p&&!a[p][c]) a[p][c]=np,p=fa[p];
if (!p) fa[np]=1;
else
{
int q=a[p][c];
if (l[p]+1==l[q]) fa[np]=q;
else
{
int nq=++nd;
l[nq]=l[p]+1;fa[nq]=fa[q];
fa[q]=fa[np]=nq;
memcpy(a[nq],a[q],sizeof(a[q]));
while (a[p][c]==q) a[p][c]=nq,p=fa[p];
}
}
return np;
}
void dfs(int x,int fa,int p)
{
int t=extend(p,col[x]);
for (int i=h[x];i;i=ed[i].next)
if (ed[i].e!=fa) dfs(ed[i].e,x,t);
}
void dp(int x)
{
u[x]=f[x]=1;
for (int i=0;i<c;i++)
if (a[x][i])
{
if (!u[a[x][i]]) dp(a[x][i]);
f[x]+=f[a[x][i]];
}
}
int main()
{
freopen("3926.in","r",stdin);
scanf("%d%d",&n,&c);
memset(h,0,sizeof(h));memset(du,0,sizeof(du));
for (i=1;i<=n;i++) scanf("%d",&col[i]);
for (i=1;i<n;i++)
{
scanf("%d%d",&s,&e);
add(e,s);add(s,e);
du[s]++;du[e]++;
}
fa[1]=0;l[1]=0;
memset(a,0,sizeof(a));
for (i=1;i<=n;i++) if (du[i]==1) dfs(i,0,1);
memset(u,0,sizeof(u));
ans=0ll;
for (i=2;i<=nd;i++) ans+=(long long)(l[i]-l[fa[i]]);
printf("%lld\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: