您的位置:首页 > 其它

bzoj3926 [Zjoi2015]诸神眷顾的幻想乡

2018-01-30 11:01 351 查看
题目链接

分析:

先强调一点,题目中特意指出:太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个

翻译一下:最多有20个叶结点

知道这道题是用广义后缀自动机解决

广义SAM中包含了所有互异的子串

所以我们应该把尽量少,但是包含所有情况的字符串扔进SAM中

一开始我想的比较简单,只要把所有叶结点到根的字符串扔进去就可以了

但是这样显然包括所有的字符串情况(画个图就知道了,没法处理折线

所以最后的解决方法就是把叶结点两两配对,

得到的字符串扔进SAM中

简单的做法:枚举每一个叶结点,以此为根,得到根到结点的路径,扔进SAM即可

最后的答案:

∑ni=1dis[i]−dis[fa[i]]

意思就是从parenti走到i有多少种不同的方式(parenti和i有相同的前缀)

tip

在以某一个结点为根dfs的时候,我们不用把每一个字符串记录下来再insert

我们可以在dfs的过程中,直接insert结点即可

但是要注意插入不同的结点时,last是不一样的

所以我们需要专门记录一下

起始时:last=root

第一次写,insert操作比较容易写错

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=100010;
int last=1,root=1,sz=1,ch[N*40][11],dis[N*40],fa[N*40];
int V
,n,c,st
,tot=0,in
;
struct node{
int y,nxt;
};
node way[N<<1];

void add(int u,int w)
{
tot++;
way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;
way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}

void insert(int x)
{
int now=ch[last][x],pre=last;
if (now)
{
if (dis[now]==dis[pre]+1) last=now;
else
{
int nows=++sz;
dis[nows]=dis[pre]+1;
memcpy(ch[nows],ch[now],sizeof(ch[now]));
fa[nows]=fa[now]; fa[now]=nows;
for (;pre&&ch[pre][x]==now;pre=fa[pre]) ch[pre][x]=nows;
last=nows;
}
}
else
{
now=++sz;
dis[now]=dis[pre]+1; last=now;
for (;pre&&!ch[pre][x];pre=fa[pre]) ch[pre][x]=now;
if (!pre) fa[now]=root;
else
{
int q=ch[pre][x];
if (dis[q]==dis[pre]+1) fa[now]=q;
else
{
int nows=++sz;
dis[nows]=dis[pre]+1;
memcpy(ch[nows],ch[q],sizeof(ch[q]));
fa[nows]=fa[q]; fa[q]=fa[now]=nows;
for (;pre&&ch[pre][x]==q;pre=fa[pre]) ch[pre][x]=nows;
}
}
}
}

void dfs(int now,int fa)
{
insert(V[now]);
int t=last;    //我们不用从头构造,只要记录一下在SAM上走到了哪里即可
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
dfs(way[i].y,now),last=t;
}

int main()
{
scanf("%d%d",&n,&c);
for (int i=1;i<=n;i++) scanf("%d",&V[i]);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); in[x]++; in[y]++;
}

for (int i=1;i<=n;i++)
if (in[i]==1)
last=root,dfs(i,0);

ll ans=0;
for (int i=1;i<=sz;i++)
ans+=(ll)dis[i]-dis[fa[i]];
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: