洛谷 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, ...
随机推荐
- EMC测试国家标准GB/T 17626
转载: 详解EMC测试国家标准GB/T 17626 - whik - 博客园 (cnblogs.com)
- netty系列之:使用netty实现支持http2的服务器
目录 简介 基本流程 CleartextHttp2ServerUpgradeHandler Http2ConnectionHandler 总结 简介 上一篇文章中,我们提到了如何在netty中配置TL ...
- (课内)信安数基RSA-level3-5
emmmm感觉其实自己对这个的理解完全不够,原理只能写出这么个东西(悲) 代码完全是 攻击方式中(1)(2)内容的实现. lambda是一种可以理解为匿名函数的写法:写在这里看起来很酷炫(bushi) ...
- 【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef
基本概念 UE4 对 UObject 对象提供垃圾回收 UE4 对原生对象不提供垃圾回收,需要手动进行清理 方式 malloc / free new / delete new与malloc的区别在于, ...
- Beta阶段第三次会议
Beta阶段第三次会议 完成工作 姓名 工作 难度 完成度 ltx 1.掌握小程序代码和相关知识2.构思小程序游客模式 轻 90% xyq 1.修改场地表格信息2.对原页面活动申请场地部分进行修改 轻 ...
- mybatis竟然报"Invalid value for getInt()"
目录 背景 场景 初探 再探 结局 背景 使用mybatis遇到一个非常奇葩的问题,错误如下: Cause: org.apache.ibatis.executor.result.ResultMapEx ...
- 实验5:开源控制器实践——POX
一.实验目的 1.能够理解 POX 控制器的工作原理: 2.通过验证POX的forwarding.hub和forwarding.l2_learning模块,初步掌握POX控制器的使用方法: 3.能够运 ...
- Django(71)图片处理器django-imagekit
介绍 ImageKit是用于处理图像的Django应用程序.如果需要从原图上生成一个长宽为50x50的图像,则需要ImageKit. ImageKit附带了一系列图像处理器,用于调整大小和裁剪等常见任 ...
- 嵌入式开发板nfs挂载
板子要开始调试了,第一个头大的问题就是调试过程中更新的文件怎么更新到板子上,以前用sd卡拷贝来来回回太浪费时间了,adb也需要接线各种连接操作. 现在板子有wifi可用,是时候把nfs共享搭起来了. ...
- 【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)
问题描述 使用 python websockets 模块作为Socket的服务端,发布到App Service for Linux环境后,发现Docker Container无法启动.错误消息为: 2 ...