正题

题目链接: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. tensorflow实现Word2vec

    # coding: utf-8 ''' Note: Step 3 is missing. That's why I left it. ''' from __future__ import absolu ...

  2. Servlet学习之复习

    Servlet的注册与运行(Servlet程序通常需要存储在<web应用程序目录>\WEB-INF\classes\目录中) 1.注册Servlet:在web.xml文件中,一个<s ...

  3. Storm重启topology,失败

    storm新加节点,重启topology后,出现错误 如果检查了CPU和内存不高后,还出现 java.lang.OutOfMemoryError: unable to create new nativ ...

  4. Gradient checking

    所需文件:本地下载 Gradient Checking Welcome to the final assignment for this week! In this assignment you wi ...

  5. 带你走进MySQL全新高可用解决方案-MGR

    ​一.初识MGR 相信很多人对MGR这个词比较陌生,其实MGR(全称 MySQL Group Replication [MySQL 组复制])是Oracle MySQL于2016年12月发布MySQL ...

  6. Python面向对象编程及内置方法

    在程序开发中,要设计一个类,通常需要满足以下三个要求: [1]类名 这类事物的名字,满足大驼峰命名法 [2]属性 这类事物具有什么样的特征 [3]方法 这类事物具有什么样的行为 定义简单的类: 定义只 ...

  7. Python中的多线程编程

    前言: 线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元) 它被包含在进程之中,是进程中的实际运作单位 一个进程中可以并发多个线程每条线程并行执行不同的任务 (线程是进程中的一个实体,是 ...

  8. 移动端ios上下滑动翻页事件失效

    移动端开发过程中,在添加上下滑动事件时候,引入了最常用的移动端库zepto.js及其touch模块,有一种现象,安卓的手机没有问题,上下滑动翻页很正常 :但是到了ios上面,好啊,上下滑动会出现弹性滚 ...

  9. ARP原理和常见分类

    路由器一个重要功能是隔离广播域.就是不转发广播包,而ARP请求就是个广播包===> arp 代理 1. ARP交互流程: (1)目的地址和主机在同一个网段(A-C): A ----------- ...

  10. C# 下载远程http文件到本地

    System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog();   ...