正题

题目链接: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. 微信小程序自定义顶部

    wxml <view style="height:{{titleHeight}}px;background:{{background}}" class="user- ...

  2. linux 的删除

    1,删除 命令行 rm -rf 文件夹名称 2,下载 wget 网址 -------------------- 查找ES进程号 ps -ef | grep elastic kill -9 3250 3 ...

  3. C# 调用C++结构体

    参考网址:C#调用C/C++动态库,封装各种复杂结构体._liguo9860的专栏-CSDN博客 现在公司要做一个使用C#程序调用C++的一个DLL库,解析文件的功能.所以在网上找了一些资料.     ...

  4. 02.SpringMVC之初体验

    1.创建Maven WEB项目 2.导入springmvc的jar包 <dependencies> <dependency> <groupId>org.spring ...

  5. 十三:Servlet3.0的异步

    servlet之前的操作同时同步的,就是按照这样的一个流程来走的: 1.请求根据一个路径路由到一个servlet中, 2.servlet获取一系列的参数 3.执行一系列的逻辑(花费时间所占的比重也更大 ...

  6. File--字节流--字符流

    File类 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. 1.构造方法 public File(String pathname) :通过将给定 ...

  7. 1.3RDD的设计与运行原理

    此文为个人学习笔记如需系统学习请访问http://dblab.xmu.edu.cn/blog/1709-2/ 提供一种通用的数据抽象 RDD典型的执行过程如下: RDD读入外部数据源(或者内存中的集合 ...

  8. centos7 netstat

    netstat 是控制台命令,它可以显示路由表.实际的网络连接以及每一个网络接口设备的状态信息.Netstat 用于显示与 IP . TCP . UDP 和 ICMP 协议相关的统计数据,一般用于检验 ...

  9. Nginx+Tomcat+Memcached实现session共享

    实验环境: server1:nginx tomcat memcached server2:tomcat memcached Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入 ...

  10. TypeScript 中函数的理解?与 JavaScript 函数的区别?

    一.是什么 函数是JavaScript 应用程序的基础,帮助我们实现抽象层.模拟类.信息隐藏和模块 在TypeScript 里,虽然已经支持类.命名空间和模块,但函数仍然是主要定义行为的方式,Type ...