正题

题目链接: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. 十五:JDBC学习入门

    一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...

  2. 关于Ubuntu18.04 linux系统下使用Tim QQ 微信

    先配上张图 步骤: 1.1 :需要安装环境deepin-wine 1.1:(你把他理解为jdk就好,没有jdk无法运行java程序,同理没有deepin-wine环境无法运行腾讯产品) 1.2 :去哪 ...

  3. 如何从 vue-element-admin 迁移到 Fantastic-admin

    // FIXME 链接更新 如果你还不知道 Fantastic-admin 是什么,那么我先用几张预览图给大家了解一番. 看来预览图,如果你感兴趣,可以点这里来详细了解并试用,这是一款完成度极高,开箱 ...

  4. Core3.1WebApi使用MongoDB

    好久没有使用MongoDB了,重新测试使用,版本不一样之前很多方法都使用不了了,下面为部分测试,下次再来更新测试 测试中使用的命令 // 新增读写的用户 db.createUser({ user:'f ...

  5. vue-父子组件之传值和单项数据流问题

    前言 我们知道 vue 中父子组件的核心概念是单项数据流问题,props 是单项传递的.那究竟什么是单项数据流问题,这篇文章来总结一下关于这个知识点的学习笔记. 正文 1.父组件传值给子组件 < ...

  6. CodeReview杂谈

    豆皮粉儿们,大家好,又见面啦,今天由字节跳动的"躬冯"带来一个 code review 的故事. 作者:躬冯 2020年元旦假期到来的时候,孙总攒了个局,又把当年一起创造过屎山的咱 ...

  7. noip模拟40

    \(\color{white}{\mathbb{名之以:海棠}}\) 考场 \(t1\) 看见题意非常简单,觉得可能是个简单题 暴力算出几个小样例右端点右移的时候左端点都是单调右移的,以为具有单调性, ...

  8. SNMP协议之序言

    最近两周公司分配一个任务:使用snmp协议做一个网管,来配置我们的产品.这可以说是我第一次听说这个协议,我问了一下周围的同事这是个什么协议,同事说"简单网络管理协议",其实这个协议 ...

  9. echo -e 命令详解

    echo在php中是输入那么在linux中是不是也是输入呢,当然echo在linux也是输入不过它的用法比php强大多了可以带参数及一些东西,下面我们来看一篇关于linux echo命令介绍及-n.- ...

  10. 获取input对应的的选项

    需求是把所有题目中的选项ABCDEF等对应保存到后台, 这个问卷中有多选项和单选项 var data = {}; $(".u-box .u-item").each(function ...