【BZOJ 1014】[JSOI2008]火星人prefix hash+splay+二分

236 篇文章 0 订阅
38 篇文章 0 订阅

一开始以为会是后缀数组之类的什么鬼东西,结果最后想了半天没想出来,结果想到hash秒出答案,所以有的时候应该考虑换一下思维方式。

因为题目要求查询和修改操作,维护一个splay,每个节点对应这个位置的字符和这棵子树的hash值,就能完成插入修改操作了,每一个询问的时候再二分最长前缀hash cheak就好了

然后。。。有一个函数忘了写return值结果BZOJ一直说我T,我改了很多常数还是T,最后才发现就过了郁闷

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 200020
#define ls ch[u][0]
#define rs ch[u][1]
#define uint unsigned int
using namespace std;
int ch[maxn][2],val[maxn],fa[maxn],size[maxn],rt,tot,len;
uint pw[maxn],h[maxn];
 
void init(int u){
    size[u]=size[ls]+size[rs]+1;
    h[u]=(h[ls]*pw[size[rs]+1]+val[u]*pw[size[rs]]+h[rs]);
}
void Rotate(int u){
    int d=u==ch[fa[u]][0];
    int f=fa[u],ff=fa[f];
    fa[u]=ff;if(ff!=0)ch[ff][f==ch[ff][1]]=u;
    ch[f][!d]=ch[u][d];fa[ch[u][d]]=f;
    ch[u][d]=f;fa[f]=u;
    init(f);init(u);
}
void splay(int u,int k=0){
    if(u==0)return;
    while(fa[u]!=k){
        int f=fa[u],ff=fa[f];
        if(ff==k){Rotate(u);break;}
        int d=u==ch[f][1],dd=f==ch[ff][1];
        if(d==dd)Rotate(f);
        else Rotate(u);
        Rotate(u);
    }
    if(k==0)rt=u;
}
//插入一个符号是x 排在第k的字符 
void insert(int u,int f,int x,int k,int d){
    if(u==0){
        tot++;
        val[tot]=h[tot]=x,fa[tot]=f,size[tot]=1,ch[f][d]=tot;
        splay(tot);
        return;
    }
    if(k>size[ls]+1)insert(rs,u,x,k-size[ls]-1,1);
    else insert(ls,u,x,k,0);
}
void change(int u,int k,int x){
    if(k==size[ls]+1){
        splay(u);val[u]=x;
        init(u);
        return;
    }
    if(k>size[ls]+1)change(rs,k-size[ls]-1,x);
    else change(ls,k,x);
}
//找k的前缀和 
uint query(int u,int k){
    if(k==0)return 0;
    if(k==size[ls]+1){
        splay(u);
        return h[ls]*pw[1]+val[u];
    }
    if(k>size[ls]+1)return query(rs,k-size[ls]-1);
    return query(ls,k);
}
uint find(int a,int b){
    if(b==0)return 0;
    if(a+b-1>len)return -a;
    return query(rt,a+b-1)-query(rt,a-1)*pw[b];
}
 
int solve(int a,int b){
    int l=0,r=min(len-a,len-b)+1,ans;
    while(l<=r){
        int mid=l+r>>1;
        if(find(a,mid)==find(b,mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
char s[maxn],c[5];
int build(int l,int r,int f){
    if(l>r)return 0;
    tot++;
    int u=tot;
    if(l==r){
        h[u]=val[u]=s[l]-'a',fa[u]=f,size[u]=1;
        return u;
    }
    int mid=l+r>>1;
    val[u]=s[mid]-'a',fa[u]=f;
    ch[u][0]=build(l,mid-1,u);
    ch[u][1]=build(mid+1,r,u);
    init(u);
    return u;
}
 
int main(){
    pw[0]=1;
    for(int i=1;i<=200000;i++)pw[i]=pw[i-1]*233;
    scanf("%s",s+1);len=strlen(s+1);int a,b,n;
    rt=build(1,len,0);
    scanf("%d",&n);
    while(n--){
        scanf("%s",c);
        if(c[0]=='Q'){
            scanf("%d%d",&a,&b);
            printf("%d\n",solve(a,b));
        }else if(c[0]=='R'){
            scanf("%d%s",&a,c);
            change(rt,a,c[0]-'a');
        }else {
            scanf("%d%s",&a,c);
            insert(rt,0,c[0]-'a',a+1,a+1>size[ch[rt][0]]+1);
            len++;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值