[ZJOI2015]诸神眷顾的幻想乡
这题的字符串匹配搬到了树上……?那不在一条链咋做啊……不会了,凉凉……
然后丽洁姐姐给我们留了一条生路……就是保证了叶子节点的个数不超过20.
树上任意一条路径,我们总能找到一个叶子节点,使得以它为根的时候这条路径在一条链上。那我们可以把每个叶子节点作为根节点来建立广义后缀自动机,最后直接统计一下不同子串数量就行了。
不过咋建立啊……不会了,凉凉……
建立方法就是找树上度数为1的点,之后从SAM的根开始建立,这里有一些不同的是,我们需要返回一下当前的新节点编号,因为我们在树上遍历的时候,只知道你要插入的字符,不知道你要往哪插……然后我们就这样遍历这棵树,插入对应字符即可。
最后统计一下不同的子串数量。因为这题是本质不同的个数,所以直接遍历所有节点,答案加上\(l[i] - l[fa[i]]\)即可。
看一下代码。
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 2000005;
const ll INF = 5e18;
int read()
{
int ans = 0,op = 1;char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
return ans * op;
}
int s[M],head[N],ecnt,n,c,x,y,deg[N];
ll ans;
struct edge
{
int next,to,from;
}e[N<<1];
struct Suffix
{
int cnt,ch[N][15],fa[N],l[N];
int extend(int p,int c)
{
int np = ++cnt;
l[np] = l[p] + 1;
while(p && !ch[p][c]) ch[p][c] = np,p = fa[p];
if(!p) {fa[np] = 1;return np;}
int q = ch[p][c];
if(l[q] == l[p] + 1) fa[np] = q;
else
{
int nq = ++cnt;
l[nq] = l[p] + 1,memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq] = fa[q],fa[q] = fa[np] = nq;
while(ch[p][c] == q) ch[p][c] = nq,p = fa[p];
}
return np;
}
void cal()
{
rep(i,2,cnt) ans += l[i] - l[fa[i]];
printf("%lld\n",ans);
}
}SAM;
void add(int x,int y)
{
e[++ecnt] = (edge){head[x],y,x};
head[x] = ecnt;
}
void dfs(int x,int fa,int g)
{
g = SAM.extend(g,s[x]);
for(int i = head[x];i;i = e[i].next) if(e[i].to != fa) dfs(e[i].to,x,g);
}
int main()
{
n = read(),c = read(),SAM.cnt = 1;
rep(i,1,n) s[i] = read();
rep(i,1,n-1) x = read(),y = read(),add(x,y),add(y,x),deg[x]++,deg[y]++;
rep(i,1,n) if(deg[i] == 1) dfs(i,0,1);
SAM.cal();
return 0;
}
当你意识到,每个上一秒都成为永恒。