当前位置:网站首页>【2022国赛模拟】白楼剑——SAM、回滚莫队、二次离线
【2022国赛模拟】白楼剑——SAM、回滚莫队、二次离线
2022-07-26 14:30:00 【偶耶XJX】
非原题链接
题目描述


题解
遇到这种找子串出现次数的题,第一时间想到 SAM。
对 S 2 S_2 S2 建出 SAM 后,我们可以在其 p a r e n t t r e e \rm parent\,tree parenttree 上做一些预处理,记录一下每个节点的祖先节点答案的最大值。然后在 S 1 S_1 S1 的对应区间内做扫描线,每次找到能够匹配的最长后缀时就可以快速求得它对答案的贡献。
从上面的暴力过程发现,我们能够较快地将询问区间扩大 1,并同时维护答案,而缩小就不行。于是在思考在线或者 P o l y ( log n ) Poly(\log n) Poly(logn) 解法未果后,我们可以自然地想到使用回滚莫队。
由于左右两边都要扩展,所以需要建出正串和反串的 SAM。由于回滚莫队往右扩展的起点只有 n \sqrt{n} n 个,所以可以暴力预处理。
往左扩展的话,预处理定位出 S 1 S_1 S1 上每个位置往后能匹配的最长串在 SAM 上的节点后,每次扩展就只需要用一个倍增跳祖先就可以定位到区间内子串对应节点,然后 O ( 1 ) O(1) O(1) 算答案即可。
于是得到一个 O ( n n log n ) O(n\sqrt{n}\log n) O(nnlogn) 的做法,我的常数显然是过不了的。
考虑把这个 log n \log n logn 给优化掉,也就是不用倍增找祖先,而用更快的方法。
一次离线已经走到尽头了,我们可以考虑二次离线。具体做法是,把上面找祖先的请求离线下来挂到 SAM 上,然后在 SAM 上做 DFS,用一个数据结构维护每种长度对应哪个祖先。
分析一下操作数发现,修改次数是 O ( n ) O(n) O(n) 的,查询次数是 O ( n n ) O(n\sqrt{n}) O(nn) 的,所以再用一个分块 O ( n ) O(\sqrt{n}) O(n) 修改, O ( 1 ) O(1) O(1) 查询即可。
最终复杂度优化为 O ( n n ) O(n\sqrt{n}) O(nn),用预先reserve技巧卡常效果更佳。
代码
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define lll __int128
#define uns unsigned
#define fi first
#define se second
#define IF (it->fi)
#define IS (it->se)
#define END putchar('\n')
#define lowbit(x) ((x)&-(x))
#define inline jzmyyds
using namespace std;
const int MAXN=1e5+3;
const ll INF=1e17;
ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){
if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt>0)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
using pii=pair<int,int>;
char s[MAXN],in[MAXN];
ll qa[33000005],fx[330][MAXN];
int QN,B,BB,n,m,q,pt[MAXN],lt[MAXN],L[MAXN],R[MAXN];
struct itn{
int ch[26],fa,len;};
int bf[MAXN],bg[330];
void cov(int L,int R,int d){
for(int e=(L-1)/BB,l=e*BB+1;l<=m;l+=BB,e++){
int r=min(m,l+BB-1);
if(l>R)break;
if(l>=L&&r<=R)bg[e]=d;
else for(int i=max(l,L),lm=min(R,r);i<=lm;i++)bf[i]=d;
}
}
void clr(int L,int R){
for(int e=(L-1)/BB,l=e*BB+1;l<=m;l+=BB,e++){
int r=min(m,l+BB-1);
if(l>R)break;
if(l>=L&&r<=R)bg[e]=0;
else for(int i=max(l,L),lm=min(R,r);i<=lm;i++)bf[i]=0;
}
}
struct SAM{
itn sam[MAXN<<1];
int las,tot,t[MAXN<<1],sv[MAXN<<1];
ll pm[MAXN<<1];
vector<int>G[MAXN<<1];
vector<pii>sk[MAXN<<1];
SAM(){
las=tot=1;}
void samadd(int c){
int p=las,np=las=++tot;sam[np].len=sam[p].len+1;
for(;p&&!sam[p].ch[c];p=sam[p].fa)sam[p].ch[c]=np;
if(!p)sam[np].fa=1;
else{
int q=sam[p].ch[c],nq;
if(sam[q].len==sam[p].len+1)sam[np].fa=q;
else{
nq=++tot,sam[nq]=sam[q],sam[nq].len=sam[p].len+1;
sam[q].fa=sam[np].fa=nq;
for(;p&&sam[p].ch[c]==q;p=sam[p].fa)sam[p].ch[c]=nq;
}
}t[np]++;
}
void pdfs(int x){
for(int&v:G[x])pdfs(v),t[x]+=t[v];
}
void build(){
for(int i=2;i<=tot;i++)G[sam[i].fa].push_back(i);
for(int i=1;i<=tot;i++)sk[i].reserve(sv[i]);
pdfs(1);
}
void dfs(int x,ll PM,bool dt){
pm[x]=PM,PM=max(PM,1ll*sam[x].len*t[x]);
if(x>1&&dt)cov(sam[sam[x].fa].len+1,sam[x].len,x);
for(auto&it:sk[x]){
if(it.fi>=sam[x].len)qa[it.se]=PM;
else{
int y=max(bf[it.fi],bg[(it.fi-1)/BB]);
qa[it.se]=max(1ll*it.fi*t[y],pm[y]);
}
}
for(int&v:G[x])dfs(v,PM,dt);
if(x>1&&dt)clr(sam[sam[x].fa].len+1,sam[x].len);
}
}P,Q;
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
*new(int)=scanf("%s%s",s+1,in+1);
n=strlen(s+1),m=strlen(in+1),B=ceil(sqrt(n)),BB=ceil(sqrt(m));
for(int i=1;i<=m;i++)P.samadd(in[i]-'a');
P.build(),P.dfs(1,0,0);
for(int l=1,e=0;l<=n;l+=B,e++){
int p=1,le=0;
for(int i=l;i<=n;i++){
int c=s[i]-'a';
while(p>1&&!P.sam[p].ch[c])p=P.sam[p].fa;
le=min(le,P.sam[p].len);
if(P.sam[p].ch[c])p=P.sam[p].ch[c],le++;
fx[e][i]=max(fx[e][i-1],max(P.pm[p],1ll*le*P.t[p]));
}
}
for(int i=m;i>0;i--)Q.samadd(in[i]-'a');
for(int i=n,le=0,p=1;i>0;i--){
int c=s[i]-'a';
while(p>1&&!Q.sam[p].ch[c])p=Q.sam[p].fa;
le=min(le,Q.sam[p].len);
if(Q.sam[p].ch[c])p=Q.sam[p].ch[c],le++;
pt[i]=p,lt[i]=le;
}
//由于问题特殊,回滚莫队可以不用排序,直接用预处理的值现算
q=read();
for(int i=1;i<=q;i++){
int l=L[i]=read(),r=R[i]=read(),el=(l-1)/B,er=(r-1)/B;
if(el==er){
for(int j=r;j>=l;j--)Q.sv[pt[j]]++;
}else for(int j=el*B+B;j>=l;j--)Q.sv[pt[j]]++;
}Q.build(),QN=0;
for(int i=1;i<=q;i++){
//二次离线
int l=L[i],r=R[i],el=(l-1)/B,er=(r-1)/B;
if(el==er){
for(int j=r;j>=l;j--)
Q.sk[pt[j]].emplace_back(min(r-j+1,lt[j]),++QN);
}else{
for(int j=el*B+B;j>=l;j--)
Q.sk[pt[j]].emplace_back(min(r-j+1,lt[j]),++QN);
}
}Q.dfs(1,0,1),QN=0;
for(int i=1;i<=q;i++){
int l=L[i],r=R[i],el=(l-1)/B,er=(r-1)/B;
ll ans=0;
if(el==er){
for(int j=r;j>=l;j--)ans=max(ans,qa[++QN]);
}else{
ans=fx[el+1][r];
for(int j=el*B+B;j>=l;j--)ans=max(ans,qa[++QN]);
}
print(ans);
}
return 0;
}
边栏推荐
- Some lightweight network models in detection and segmentation (share your own learning notes)
- maya将模型导入到unity
- sqlDeveloper工具快速入门
- 如何评价测试质量?
- @A thousand lines of work, ride the cloud together!
- 【无标题】
- 键盘快捷键操作电脑(自己遇到不会的)
- Use of URL download resources
- Disease knowledge discovery based on spo semantic triples
- 如何做 APP 升级测试 ?
猜你喜欢

Leetcode215 the kth largest element (derivation of quick sort partition function)

31. Opinion based relational pivoting forcross domain aspect term extraction reading notes

【整数规划】

maya将模型导入到unity

OpenCV中图像算术操作与逻辑操作

31. Opinion-based Relational Pivoting forCross-domain Aspect Term Extraction 阅读笔记

Embedded development: skills of debugging embedded software

GOM login configuration free version generate graphic tutorial

Use of LINGO software

GOM登录器配置免费版生成图文教程
随机推荐
Would you please refer to the document of Database specification?
UE4 smart pointer and weak pointer
关于存储芯片的入门基础知识
1对1直播源码——1对1语音聊天源码
请问下大家,flink sql有没有办法不输出update_before?
嵌入式开发:调试嵌入式软件的技巧
Annotation and reflection
【深度学习】全连接网络
键盘快捷键操作电脑(自己遇到不会的)
[ostep] 03 virtualized CPU - restricted direct execution mechanism
基于用户画像的在线健康社区用户流失预测研究
Leetcode question type priority queue (TOPK question)
OLAP (business) - transaction analysis (query)
C# 常用功能整合
Comparison between agile development and Devops
【1.2.投资的收益和风险】
[deep learning] fully connected network
Multithreading - thread pool
1-to-1 live broadcast source code - 1-to-1 voice chat source code
Redis data operation