洛谷 P7879 -「SWTR-07」How to AK NOI?(后缀自动机+线段树维护矩乘)
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}\),那么
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?(后缀自动机+线段树维护矩乘)的更多相关文章
- 洛谷.5284.[十二省联考2019]字符串问题(后缀自动机 拓扑 DP)
LOJ BZOJ 洛谷 对这题无话可说,确实比较...裸... 像dls说的拿拓扑和parent树一套就能出出来了... 另外表示BZOJ Rank1 tql... 暴力的话,由每个\(A_i\)向它 ...
- [洛谷P3701]「伪模板」主席树
题目大意:太暴力了,就不写了,看这儿 题解:对于每个$byx$的人,从源点向人连边,容量为此人的寿命. 对于每个手气君的人,从人向汇点连边,容量为此人的寿命. 对于每个$byx$的人与手气君的人,如果 ...
- LOJ 3119: 洛谷 P5400: 「CTS2019 | CTSC2019」随机立方体
题目传送门:LOJ #3119. 题意简述: 题目说的很清楚了. 题解: 记恰好有 \(i\) 个极大的数的方案数为 \(\mathrm{cnt}[i]\),则答案为 \(\displaystyle\ ...
- LOJ 3120: 洛谷 P5401: 「CTS2019 | CTSC2019」珍珠
题目传送门:LOJ #3120. 题意简述: 称一个长度为 \(n\),元素取值为 \([1,D]\) 的整数序列是合法的,当且仅当其中能够选出至少 \(m\) 对相同元素(不能重复选出元素). 问合 ...
- 洛谷P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)
题意 题目链接 Sol 广义SAM的板子题. 首先叶子节点不超过20,那么可以直接对每个叶子节点为根的子树插入到广义SAM中. 因为所有合法的答案一定是某个叶子节点为根的树上的一条链,因此这样可以统计 ...
- 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)
题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)
传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...
- 洛谷 P4710 「物理」平抛运动
洛谷 P4710 「物理」平抛运动 洛谷传送门 题目描述 小 F 回到班上,面对自己 28 / 110 的物理,感觉非常凉凉.他准备从最基础的力学学起. 如图,一个可以视为质点的小球在点 A(x_0, ...
随机推荐
- 【实验向】问题:假设计算机A和计算机B通信,计算机A给计算机B发送一串16个字节的二进制字节串,以数组形式表示:
问题: 假设计算机A和计算机B通信,计算机A给计算机B发送一串16个字节的二进制字节串,以数组形式表示: unsigned char[16] = {0x3f, 0xa0, 0x00, 0x00, 0x ...
- Golang通脉之接口
接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类型. interface是 ...
- Java:基本概念小记
Java:基本概念 一些基本 Java 概念,做一个小小小小的记录 面向对象&面向过程 面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征.行为特征抽象出来,描述成计算机 ...
- Scrum Meeting 16
第16次例会报告 日期:2021年06月11日 会议主要内容概述: 最后一次例会 一.进度情况 我们采用日报的形式记录每个人的具体进度,链接Home · Wiki,如下记录仅为保证公开性: 组员 负责 ...
- [技术博客] BeautifulSoup4分析网页
[技术博客] BeautifulSoup4分析网页 使用BeautifulSoup4进行网页文本分析 前言 进行网络爬虫时我们需要从网页源代码中提取自己所需要的信息,分析整理后存入数据库中. 在pyt ...
- RocketMQ源码详解 | Broker篇 · 其一:线程模型与接收链路
概述 在上一节 RocketMQ源码详解 | Producer篇 · 其二:消息组成.发送链路 中,我们终于将消息发送出了 Producer,在短暂的 tcp 握手后,很快它就会进入目的 Broker ...
- 企业级BI为什么这么难做?
本人长期在银行内从事数据线相关工作,亲眼目睹过多个企业级BI(非部门级BI)产品从上线试用.全行推广.然后衰败没落,再替换到下一个BI产品重复此过程.企业内没有任何一个BI产品即能长期运行,又能赢得非 ...
- Django(71)图片处理器django-imagekit
介绍 ImageKit是用于处理图像的Django应用程序.如果需要从原图上生成一个长宽为50x50的图像,则需要ImageKit. ImageKit附带了一系列图像处理器,用于调整大小和裁剪等常见任 ...
- python 模块 hashlib(提供多个不同的加密算法)
hashlib是涉及安全散列和消息摘要,提供多个不同的加密算法借口,如SHA1.SHA224.SHA256.SHA384.SHA512.MD5等. import hashlib m = hashlib ...
- Python技法4:闭包
闭包:用函数代替类 有时我们会定义只有一个方法(除了__init__()之外)的类,而这种类可以通过使用闭包(closure)来替代.闭包是被外层函数包围的内层函数,它能够获取外层函数范围中的变量(即 ...