- 题意:P4036
Q x y:计算LCQ(x,y)就是从x开始的后缀与y开始的后缀的最长公共前缀
R x d:将第x个改为d
I x d:在x后面加入d - 思路
如果我们想到了字符串哈希(是一种可加可减,很全面的算法)。
然后我们又想到了二分,这道题就完了!
可惜我都没想到!
然后二分后,判断我们发现串是一个动态序列,于是我们很容易用splay维护区间字符串哈希值(这里用的是自然溢出)
所以一道题的思路再复杂也绕不到哪去,你觉得越想越离谱的时候就要知道自己想的多半是错的
当然我说的是的线段树
如果没有操作三的话,线段树静态维护。
因此先离线,我们把I操作加入的点和原来的点按照所有操作结束后的顺序揉在一起,然后我们从后往前枚举操作,我们用树状数组来维护每一位是否(0/1)当前操作后存在,显然一开始就所有数都存在,遇到“I”操作找到rk=对应点+1--,同时存一下每个点的新编号。
不过最后的问题是线段树中会有空的点。其实不影响以为合并的时候是按根据sz来合并的,然后记得sz也是需要动态添加删除的(我忘了,调了好久)
- code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef unsigned long long ull;
struct query {int opt,x,y;}Q[N];
struct seg {int l,r;ull sum;}T[N];
struct node {ull sum;int cnt;};
ull pw[N],seed=27;
char s[N];
int lf[N],c[N],n,m,mark[N],sz[N];
int lowbit(int x) {return x&(-x);}
void Update(int x,int w) {for(;x<=n;x+=lowbit(x))c[x]+=w;}
int Ask(int x) {int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
void P_up(int x) {int ls=x<<1,rs=ls|1;T[x].sum=T[ls].sum*pw[sz[rs]]+T[rs].sum;sz[x]=sz[ls]+sz[rs];}
void Build(int x,int l,int r) {
T[x]=(seg){l,r,0};
if(l==r) {if(lf[l])sz[x]=1,T[x].sum=s[lf[l]]-'a';return;}
int mid=(l+r)>>1,ls=x<<1,rs=x<<1|1;
Build(ls,l,mid),Build(rs,mid+1,r);
P_up(x);
}
node Hash(int x,int l,int r) {
if(l<=T[x].l&&T[x].r<=r) {return (node){T[x].sum,sz[x]};}
ull sum=0;int cnt=0,mid=(T[x].l+T[x].r)>>1,ls=x<<1,rs=ls|1;
if(l<=mid) {node t=Hash(ls,l,r);sum+=t.sum;cnt+=t.cnt;}
if(r>mid) {node t=Hash(rs,l,r);sum=sum*pw[t.cnt]+t.sum;cnt+=t.cnt;}
return (node){sum,cnt};
}
void Change(int x,int p,int d,int ad) {
if(T[x].l==T[x].r) {T[x].sum=d;sz[x]+=ad;return;}
int mid=(T[x].l+T[x].r)>>1;
if(p<=mid) Change(x<<1,p,d,ad);
else Change(x<<1|1,p,d,ad);
P_up(x);
}
int _fd(int x) {
int l=1,r=n,best=n;
while(l<=r) {
int mid=(l+r)>>1;
if(Ask(mid)<x) l=mid+1;
else {best=mid;r=mid-1;}
}
return best;
}
void init() {
scanf("%s",s+1);n=strlen(s+1);
scanf("%d",&m);
for(int i=1;i<=m;i++) {
char ch[3],d[3];int x,y;
scanf("%s%d",ch,&x);
if(ch[0]=='Q') {scanf("%d",&y);Q[i]=(query){0,x,y};}
else if(ch[0]=='R') {scanf("%s",d);y=d[0]-'a';Q[i]=(query){1,x,y};}
else {scanf("%s",d);++n;y=d[0]-'a';Q[i]=(query){2,x,y};}
}
pw[0]=1;for(int i=1;i<=n;i++)Update(i,1),pw[i]=pw[i-1]*seed;
for(int i=m;i;i--) {
if(!Q[i].opt) Q[i].y=_fd(Q[i].y);
if(Q[i].opt==2) Q[i].x=_fd(Q[i].x+1)-1,Update(Q[i].x+1,-1);
else Q[i].x=_fd(Q[i].x);
}
int lst=0;
for(int i=1;i<=n;i++) if(Ask(i)>lst) lf[i]=++lst;
// for(int i=1;i<=n;i++) printf("%d ",lf[i]);puts("");
Build(1,1,n);
}
void solve() {
int nn=strlen(s+1);
for(int i=1;i<=m;i++) {
if(!Q[i].opt) {
int x=Q[i].x,rx=Ask(x),y=Q[i].y,ry=Ask(y);if(x>y) swap(x,y),swap(rx,ry);
int l=1,r=nn-ry+1,best=0;
while(l<=r) {
int mid=(l+r)>>1;
if(Hash(1,x,_fd(rx+mid-1)).sum==Hash(1,y,_fd(ry+mid-1)).sum) best=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",best);
}
else if(Q[i].opt==1) {Change(1,Q[i].x,Q[i].y,0);}
else {Update(Q[i].x+1,1),Change(1,Q[i].x+1,Q[i].y,1);nn++;}
}
}
int main() {
init();
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人