正题

题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1600


题目大意

给出一个字符串\(s\),每次在最后插入一个字符后求它的所有分别子串构出的\(fail\)树的深度和。

\(1\leq Q\leq 10^5\)


解题思路

考虑两个相等的子串长度为\(len\),那么以后面那个子串末尾结尾的\(fail\)有\(len\)种左端点的情况是指向前面那个子串的。

新插入后所有串的后缀都是新的子串,考虑如何统计这些串的答案,首先不考虑最后一个位置那么深度和就是前面那次新加的深度和。现在只需要计算新插入那个字符在这\(n\)个串中的贡献,我们可以找出所有和这些串的所有后缀相同的子串都会产生贡献,这个可以用\(SAM\)统计。

所以可以考虑先把完整的串的\(SAM\)建出来再考虑做法,每次插入一个字符串的时候先查询它在\(parents\)树上到根的路径的边权乘上边的长度和,然后再向这条路径上每条边的权值加一。

注意到要路径加权求和,所以要加一个树剖就可以了

时间复杂度\(O(n\log^2 n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=4e5+10,P=1e9+7;
struct node{
int to,next;
}a[N];
int n,cnt,last,tot,dfc,p[N],ls[N];
int siz[N],son[N],top[N],dfn[N],rfn[N];
int fa[N],ch[N][26];ll len[N];
char s[N];bool v[N];
struct SegTree{
ll w[N<<2],lazy[N<<2];
void Downdata(int x,int L,int R){
if(!lazy[x])return;
int mid=(L+R)>>1;
w[x*2]=(w[x*2]+lazy[x]*(len[dfn[mid]]-len[dfn[L-1]]))%P;
w[x*2+1]=(w[x*2+1]+lazy[x]*(len[dfn[R]]-len[dfn[mid]]))%P;
lazy[x*2]+=lazy[x];lazy[x*2+1]+=lazy[x];
lazy[x]=0;return;
}
void Change(int x,int L,int R,int l,int r){
if(L==l&&R==r){
(w[x]+=len[dfn[R]]-len[dfn[L-1]])%=P;
lazy[x]++;return;
}
int mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)Change(x*2,L,mid,l,r);
else if(l>mid) Change(x*2+1,mid+1,R,l,r);
else Change(x*2,L,mid,l,mid),Change(x*2+1,mid+1,R,mid+1,r);
w[x]=(w[x*2]+w[x*2+1]);
return;
}
ll Ask(int x,int L,int R,int l,int r){
if(L==l&&R==r)return w[x];
int mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)return Ask(x*2,L,mid,l,r);
if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
return (Ask(x*2,L,mid,l,mid)+Ask(x*2+1,mid+1,R,mid+1,r))%P;
}
}T;
void Insert(int c){
int p=last,np=last=++cnt;
len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[np]=q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
v[np]=1;return;
}
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void dfs(int x){
siz[x]=1;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
dfs(y);siz[x]+=siz[y];
len[y]=len[y]-len[x];
if(siz[y]>siz[son[x]])son[x]=y;
}
return;
}
void dfs2(int x){
dfn[++dfc]=x;rfn[x]=dfc;
if(son[x]){
top[son[x]]=top[x];
dfs2(son[x]);
}
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==son[x])continue;
top[y]=y;dfs2(y);
}
return;
}
void print(int x)
{if(x>9)print(x/10);putchar(x%10+48);return;}
signed main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf("%d",&n);
scanf("%s",s+1);last=cnt=1;
for(int i=1;i<=n;i++)
Insert(s[i]-'a'),p[i]=last;
for(int i=2;i<=cnt;i++)addl(fa[i],i);
top[1]=1;dfs(1);dfs2(1);
ll k=0,ans=0;
for(int i=1;i<=cnt;i++)
len[dfn[i]]=(len[dfn[i]]+len[dfn[i-1]])%P;
for(int i=1;i<=n;i++){
int x=p[i];
while(x){
k=(k+T.Ask(1,1,cnt,rfn[top[x]],rfn[x]))%P;
x=fa[top[x]];
}
ans=(ans+k)%P;x=p[i];
while(x){
T.Change(1,1,cnt,rfn[top[x]],rfn[x]);
x=fa[top[x]];
}
print((ans+P)%P);
putchar('\n');
}
return 0;
}

51nod1600-Simple KMP【SAM,树链剖分】的更多相关文章

  1. 【UOJ#435】【集训队作业2018】Simple Tree 分块+树链剖分

    题目大意: 有一棵有根树,根为 1 ,点有点权.现在有 m 次操作,操作有 3 种:1 x y w ,将 x 到 y 的路径上的点点权加上 w (其中 w=±1w=±1 ):2 x y ,询问在 x ...

  2. 2019.03.09 bzoj4999: This Problem Is Too Simple!(树链剖分+线段树动态开点)

    传送门 题意:给一颗树,每个节点有个初始值,要求支持将i节点的值改为x或询问i节点到j节点的路径上有多少个值为x的节点. 思路: 考虑对每种颜色动态开点,然后用树剖+线段树维护就完了. 代码: #in ...

  3. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  4. UOJ#435. 【集训队作业2018】Simple Tree 树链剖分,分块

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ435.html 前言 分块题果然是我这种蒟蒻写不动的.由于种种原因,我写代码的时候打错了很多东西,最致命的是数组开小了.* ...

  5. 【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

    题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) ...

  6. HDU 4897 Little Devil I(树链剖分)(2014 Multi-University Training Contest 4)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4897 Problem Description There is an old country and ...

  7. CF 191C Fools and Roads lca 或者 树链剖分

    They say that Berland has exactly two problems, fools and roads. Besides, Berland has n cities, popu ...

  8. Codeforces Round #329 (Div. 2) D. Happy Tree Party 树链剖分

    D. Happy Tree Party Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/593/p ...

  9. hdu 5052 树链剖分

    Yaoge’s maximum profit Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/ ...

随机推荐

  1. VMware中安装CentOS Linux release 7.4.1708 (Core)

    本篇文章主要介绍了VMware安装Centos7超详细过程(图文),具有一定的参考价值,感兴趣的小伙伴们可以参考一下 1.软硬件准备 软件:推荐使用VMwear,我用的是VMwear 12 镜像:Ce ...

  2. SpringCloud降级熔断 Hystrix

    1.分布式核心知识之熔断.降级讲解 简介:系统负载过高,突发流量或者网络等各种异常情况介绍,常用的解决方案 1.熔断:         保险丝,熔断服务,为了防止整个系统故障,包含子和下游服务 下单服 ...

  3. Maven使用--基本入门

    maven学习(上)- 基本入门用法 转载自:https://www.cnblogs.com/yjmyzz/p/3495762.html 参考: http://www.cnblogs.com/dave ...

  4. 恶意软件开发——shellcode执行的几种常见方式

    一.什么是shellcode? shellcode是一小段代码,用于利用软件漏洞作为有效载荷.它之所以被称为"shellcode",是因为它通常启动一个命令shell,攻击者可以从 ...

  5. Python3-sqlalchemy-orm 创建关联表带外键并查询数据

    #-*-coding:utf-8-*- #__author__ = "logan.xu" import sqlalchemy from sqlalchemy import crea ...

  6. appium+python运行自动化测试提示“find_element() takes from 1 to 3 positional arguments but 14 were given”错误

    1.运行后提示"find_element() takes from 1 to 3 positional arguments but 14 were given",在网上找了很多解决 ...

  7. PyCharm——滚动鼠标调整字体大小

  8. MySQL——MySQL体系结构

    1.连接层 2.SQL层: (1)将接收到的SQL语句,解析成执行计划 (2)查询优化器 ---->选择最优的执行计划,交给存储引擎 (3)查询缓存: 缓存执行计划 (4)附加功能:权限. 语法 ...

  9. jq的slideToggle效果

    slideToggle() 方法通过使用滑动效果(高度变化)来切换元素的可见状态. 如果被选元素是可见的,则隐藏这些元素,如果被选元素是隐藏的,则显示这些元素. 例子:一个简单的下拉菜单效果----& ...

  10. Reader字符输入流和InputStreamReader和FileReader

    1.FileReader  extends InputStreamReader extends Reader 其中,Reader字符输入流和InputStreamReader和FileReader为字 ...