洛谷题面传送门

orz 一发出题人(话说我 AC 这道题的时候,出题人好像就坐在我的右侧呢/cy/cy)

考虑一个很 naive 的 DP,\(dp_i\) 表示 \([l,i]\) 之间的字符串是否可以被识别,转移就枚举上一段的终止为止,然后 SAM/哈希判断子串是否在 \(s\) 中出现过。

注意到一个事实:所有长度 \(>2k\) 的字符串都可以由长度 \(\ge k\) 的字符串拼成,也就是说只有长度在 \([k,2k]\) 的字符串是有用的,故每次转移只用枚举 \(k+1\) 个转移点。注意到 \(k\) 的数据范围很小,因此我们可以考虑矩阵优化转移,即对于大小分别为 \(n\times m\) 和 \(m\times k\) 的矩阵 \(A,B\),定义其乘积 \(C\) 满足 \(C_{i,j}=\text{or}_{t=1}^mA_{i,t}\land B_{t,j}\),那么

\[\begin{bmatrix}
dp_{i}&dp_{i-1}&\cdots&dp_{i-2k+1}
\end{bmatrix}
=
\begin{bmatrix}
dp_{i-1}&dp_{i-2}&\cdots&dp_{i-2k}
\end{bmatrix}
\times
\begin{bmatrix}
g_1&1&0&0&\cdots&0\\
g_2&0&1&0&\cdots&0\\
g_3&0&0&1&\cdots&0\\
\vdots&\vdots&\vdots&\vdots&\ddots&\vdots\\
g_{2k-1}&0&0&0&\cdots&1\\
g_{2k}&0&0&0&\cdots&0
\end{bmatrix}
\]

其中 \(g_j\) 表示 \([i-j+1,i]\) 是否为 \(s\) 的子串。

显然每次修改一个区间最多改变 \(r-l+1+2k\) 个位置的转移矩阵,因此我们有一个很自然的想法:线段树维护矩阵乘积,每次暴力修改线段树对应的位置的值,求一个位置的转移矩阵可用 SAM。查询就求一遍区间矩阵乘积,这样复杂度大概是 \((L+qk)·k^3\log n+qk^3\log n\),显然无法通过,不过这里矩阵每个元素都是 0/1,因此可以用位运算加速实现 \(k^2\) 矩阵乘法,这样复杂度可达到 \((L+qk)·k^2\log n+qk^2\log n\),还是无法通过。再观察到每次修改是一段区间,因此考虑对这一段区间上的叶子节点到根节点路径的并上的节点,重新计算它们转移矩阵的乘积,这样假设待重构的区间长度为 \(len\),每次重构需要进行矩阵乘法的次数大概是 \(\sum\limits_{n}\lfloor\dfrac{len}{n}\rfloor=\mathcal O(len)\) 级别的,这样加号前面的 \(\log n\) 可以省去,复杂度就达到了 \((L+qk)·k^2+qk^2\log n\),可以通过。

const int MAXN=3e6;
const int MAXM=3e5;
const int MAXP=6e6;
const int MAXK=16;
int n,m,k,qu;char s[MAXN+5],t[MAXM+5];
int ch[MAXP+5][9],len[MAXP+5],lnk[MAXP+5],cur=1,ncnt=1;
void extend(char c){
int id=c-'a',nw=++ncnt,p=cur;
len[nw]=len[cur]+1;cur=nw;
while(p&&!ch[p][id]) ch[p][id]=nw,p=lnk[p];
if(!p) return lnk[nw]=1,void();
int q=ch[p][id];
if(len[q]==len[p]+1) return lnk[nw]=q,void();
int cl=++ncnt;len[cl]=len[p]+1;
lnk[cl]=lnk[q];lnk[q]=lnk[nw]=cl;
for(int i=0;i<9;i++) ch[cl][i]=ch[q][i];
while(p&&ch[p][id]==q) ch[p][id]=cl,p=lnk[p];
}
struct mat{
int a[MAXK+2];
mat(){memset(a,0,sizeof(a));}
mat operator *(const mat &rhs){
mat res;
for(int i=0;i<k*2;i++) for(int j=0;j<k*2;j++)
if(a[i]>>j&1) res.a[i]|=rhs.a[j];
return res;
}
void print(){
for(int i=0;i<k+k;i++) for(int j=0;j<k+k;j++)
printf("%d%c",a[i]>>j&1,"\0\n"[j==k+k-1]);
}
};
int lf[MAXM+5],used[MAXM*4+5];
mat calc(int l){
mat ret;
for(int i=0;i+1<k+k;i++) ret.a[i]|=1<<i+1;
int curp=1;
for(int i=1;i<=min(l,k+k);i++){
if(!ch[curp][t[l-i+1]-'a']) break;
curp=ch[curp][t[l-i+1]-'a'];
if(i>=k) ret.a[i-1]|=1;
}
// printf("calc %d\n",l);
// ret.print();
return ret;
}
namespace segtree{
struct node{int l,r;mat v;} s[MAXM*4+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;
if(l==r){s[k].v=calc(l),lf[l]=k;return;}
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
s[k].v=s[k<<1].v*s[k<<1|1].v;
}
void rebuild(int k){
if(!used[k]) return;used[k]=0;
if(s[k].l==s[k].r) return;
rebuild(k<<1);rebuild(k<<1|1);
s[k].v=s[k<<1].v*s[k<<1|1].v;
}
mat query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].v;
int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return query(k<<1,l,mid)*query(k<<1|1,mid+1,r);
}
}
void rebuild(int l,int r){
static char buf[MAXM+5];scanf("%s",buf+1);
for(int i=l;i<=r;i++) t[i]=buf[i-l+1];
for(int i=l;i<=min(r+k+k,m);i++){
for(int j=lf[i];j;j>>=1) used[j]=1;
segtree::s[lf[i]].v=calc(i);
} segtree::rebuild(1);
}
int main(){
// freopen("passage.in","r",stdin);
// freopen("passage.out","w",stdout);
scanf("%*d%s%s%d%d",s+1,t+1,&k,&qu);
n=strlen(s+1);m=strlen(t+1);
for(int i=n;i;i--) extend(s[i]);
segtree::build(1,1,m);
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){
int l,r;scanf("%d%d",&l,&r);
rebuild(l,r);
} else {
int l,r;scanf("%d%d",&l,&r);
mat v=segtree::query(1,l,r);
printf("%s\n",(v.a[0]&1)?"Yes":"No");
// v.print();
}
}
return 0;
}

洛谷 P7879 -「SWTR-07」How to AK NOI?(后缀自动机+线段树维护矩乘)的更多相关文章

  1. 洛谷.5284.[十二省联考2019]字符串问题(后缀自动机 拓扑 DP)

    LOJ BZOJ 洛谷 对这题无话可说,确实比较...裸... 像dls说的拿拓扑和parent树一套就能出出来了... 另外表示BZOJ Rank1 tql... 暴力的话,由每个\(A_i\)向它 ...

  2. [洛谷P3701]「伪模板」主席树

    题目大意:太暴力了,就不写了,看这儿 题解:对于每个$byx$的人,从源点向人连边,容量为此人的寿命. 对于每个手气君的人,从人向汇点连边,容量为此人的寿命. 对于每个$byx$的人与手气君的人,如果 ...

  3. LOJ 3119: 洛谷 P5400: 「CTS2019 | CTSC2019」随机立方体

    题目传送门:LOJ #3119. 题意简述: 题目说的很清楚了. 题解: 记恰好有 \(i\) 个极大的数的方案数为 \(\mathrm{cnt}[i]\),则答案为 \(\displaystyle\ ...

  4. LOJ 3120: 洛谷 P5401: 「CTS2019 | CTSC2019」珍珠

    题目传送门:LOJ #3120. 题意简述: 称一个长度为 \(n\),元素取值为 \([1,D]\) 的整数序列是合法的,当且仅当其中能够选出至少 \(m\) 对相同元素(不能重复选出元素). 问合 ...

  5. 洛谷P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

    题意 题目链接 Sol 广义SAM的板子题. 首先叶子节点不超过20,那么可以直接对每个叶子节点为根的子树插入到广义SAM中. 因为所有合法的答案一定是某个叶子节点为根的树上的一条链,因此这样可以统计 ...

  6. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  7. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  8. 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)

    传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...

  9. 洛谷 P4710 「物理」平抛运动

    洛谷 P4710 「物理」平抛运动 洛谷传送门 题目描述 小 F 回到班上,面对自己 28 / 110 的物理,感觉非常凉凉.他准备从最基础的力学学起. 如图,一个可以视为质点的小球在点 A(x_0, ...

随机推荐

  1. 手把手教你学Dapr - 1. .Net开发者的大时代

    Dapr全称 Distributed Application Runtime,分布式应用运行时 Dapr的口号 简化云原生应用开发,聚焦在应用的核心逻辑,让代码简单.可移植 Dapr的目标 最佳实践的 ...

  2. Java字符串分割函数split源码分析

    spilt方法作用 以所有匹配regex的子串为分隔符,将input划分为多个子串. 例如: The input "boo:and:foo", for example, yield ...

  3. MySQL:怒刷牛客网“sql实战”

    MySQL:怒刷牛客网"sql实战" 在对MySQL有一定了解后,抽空刷了一下 牛客网上的 数据库SQL 实战,在此做一点小小的记录 SQL1 查找最晚入职员工的所有信息 sele ...

  4. Alpha-技术规格说明书

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 团队项目-计划-功能规格说明书 一.架构与技术栈 1.整体架构 本项目的整体架构如上图所示.下面我们将对涉及 ...

  5. Spring动态添加定时任务

    Spring动态添加定时任务 一.背景 二.需求和实现思路 1.能够动态的添加一个定时任务. 2.能够取消定时任务的执行. 3.动态的修改任务执行的时间. 4.获取定时任务执行的异常 三.代码实现 四 ...

  6. Noip模拟11 2021.7.11

    T1 math 其实看看题面,看看给的那机组数据即可看出规律了(然而当时并没有,只是发现模数的循环节,存了个vector,接下来就暴力了) 有个柿子: 其实就是裴蜀定理. 然后想一想的话就是 那么只要 ...

  7. 2021.9.22考试总结[NOIP模拟59]

    T1 柱状图 关于每个点可以作出两条斜率绝对值为\(1\)的直线. 将绝对值拆开,对在\(i\)左边的点\(j\),\(h_i-i=h_j-j\),右边则是把减号换成加号. 把每个点位置为横坐标,高度 ...

  8. 震惊,hzoi的考试竟然折磨简单,活到爆!

    众所周知,hzoi的考试题非常"简单",那么究竟有多简单呢?最近,一位外国小哥开发出了hzoi的考试竟然折磨简单,活到爆!的方法,这究竟是怎么一回事呢?快和小编一起来看看吧- 满分 ...

  9. Hash算法:双重散列

    双重散列是线性开型寻址散列(开放寻址法)中的冲突解决技术.双重散列使用在发生冲突时将第二个散列函数应用于键的想法. 此算法使用: (hash1(key) + i * hash2(key)) % TAB ...

  10. Serverless 工程实践|自建 Apache OpenWhisk 平台

    作者 | 刘宇(江昱) 前言:OpenWhisk 是一个开源.无服务器的云平台,可以在运行时容器中通过执行扩展的代码响应各种事件,而无须用户关心相关的基础设施架构. OpenWhisk 简介 Open ...