51nod1600-Simple KMP【SAM,树链剖分】
正题
题目链接: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,树链剖分】的更多相关文章
- 【UOJ#435】【集训队作业2018】Simple Tree 分块+树链剖分
题目大意: 有一棵有根树,根为 1 ,点有点权.现在有 m 次操作,操作有 3 种:1 x y w ,将 x 到 y 的路径上的点点权加上 w (其中 w=±1w=±1 ):2 x y ,询问在 x ...
- 2019.03.09 bzoj4999: This Problem Is Too Simple!(树链剖分+线段树动态开点)
传送门 题意:给一颗树,每个节点有个初始值,要求支持将i节点的值改为x或询问i节点到j节点的路径上有多少个值为x的节点. 思路: 考虑对每种颜色动态开点,然后用树剖+线段树维护就完了. 代码: #in ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- UOJ#435. 【集训队作业2018】Simple Tree 树链剖分,分块
原文链接www.cnblogs.com/zhouzhendong/p/UOJ435.html 前言 分块题果然是我这种蒟蒻写不动的.由于种种原因,我写代码的时候打错了很多东西,最致命的是数组开小了.* ...
- 【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) ...
- 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 ...
- CF 191C Fools and Roads lca 或者 树链剖分
They say that Berland has exactly two problems, fools and roads. Besides, Berland has n cities, popu ...
- 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 ...
- hdu 5052 树链剖分
Yaoge’s maximum profit Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/ ...
随机推荐
- 十五:JDBC学习入门
一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...
- 关于Ubuntu18.04 linux系统下使用Tim QQ 微信
先配上张图 步骤: 1.1 :需要安装环境deepin-wine 1.1:(你把他理解为jdk就好,没有jdk无法运行java程序,同理没有deepin-wine环境无法运行腾讯产品) 1.2 :去哪 ...
- 如何从 vue-element-admin 迁移到 Fantastic-admin
// FIXME 链接更新 如果你还不知道 Fantastic-admin 是什么,那么我先用几张预览图给大家了解一番. 看来预览图,如果你感兴趣,可以点这里来详细了解并试用,这是一款完成度极高,开箱 ...
- Core3.1WebApi使用MongoDB
好久没有使用MongoDB了,重新测试使用,版本不一样之前很多方法都使用不了了,下面为部分测试,下次再来更新测试 测试中使用的命令 // 新增读写的用户 db.createUser({ user:'f ...
- vue-父子组件之传值和单项数据流问题
前言 我们知道 vue 中父子组件的核心概念是单项数据流问题,props 是单项传递的.那究竟什么是单项数据流问题,这篇文章来总结一下关于这个知识点的学习笔记. 正文 1.父组件传值给子组件 < ...
- CodeReview杂谈
豆皮粉儿们,大家好,又见面啦,今天由字节跳动的"躬冯"带来一个 code review 的故事. 作者:躬冯 2020年元旦假期到来的时候,孙总攒了个局,又把当年一起创造过屎山的咱 ...
- noip模拟40
\(\color{white}{\mathbb{名之以:海棠}}\) 考场 \(t1\) 看见题意非常简单,觉得可能是个简单题 暴力算出几个小样例右端点右移的时候左端点都是单调右移的,以为具有单调性, ...
- SNMP协议之序言
最近两周公司分配一个任务:使用snmp协议做一个网管,来配置我们的产品.这可以说是我第一次听说这个协议,我问了一下周围的同事这是个什么协议,同事说"简单网络管理协议",其实这个协议 ...
- echo -e 命令详解
echo在php中是输入那么在linux中是不是也是输入呢,当然echo在linux也是输入不过它的用法比php强大多了可以带参数及一些东西,下面我们来看一篇关于linux echo命令介绍及-n.- ...
- 获取input对应的的选项
需求是把所有题目中的选项ABCDEF等对应保存到后台, 这个问卷中有多选项和单选项 var data = {}; $(".u-box .u-item").each(function ...