[BZOJ1014][JSOI2008]火星人prefix(hash+splay)

211 篇文章 0 订阅
33 篇文章 0 订阅

题目描述

传送门

题解

用splay维护一段区间的hash值,然后每一次查询的时候二分+判定就行了
算hash值update的时候只需要维护一下左右子树的大小
注意插入一个前驱一个后继,编号不要算错了

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 150005
#define UL unsigned long long

char s[N];
int n,root,m,x,y,px,py,ans;
int f[N],ch[N][2],size[N];char key[N];
UL mi[N],hash[N];

const UL S=2000001001LL;
void update(int x)
{
    UL now=0;size[x]=0;
    if (ch[x][1]) now+=hash[ch[x][1]],size[x]+=size[ch[x][1]];
    now+=(UL)key[x]*mi[size[x]];++size[x];
    if (ch[x][0]) now+=hash[ch[x][0]]*mi[size[x]],size[x]+=size[ch[x][0]];
    hash[x]=now;
}
int build(int l,int r,int fa)
{
    if (l>r) return 0;
    int mid=(l+r)>>1;
    f[mid]=fa;key[mid]=s[mid-1];
    int ls=build(l,mid-1,mid);
    int rs=build(mid+1,r,mid);
    ch[mid][0]=ls,ch[mid][1]=rs;
    update(mid);
    return mid;
}
int get(int x)
{
    return ch[f[x]][1]==x;
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],wh=get(x);
    ch[old][wh]=ch[x][wh^1];
    if (ch[old][wh]) f[ch[old][wh]]=old;
    ch[x][wh^1]=old;
    f[old]=x;
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
    update(old);
    update(x);
}
void splay(int x,int tar)
{
    for (int fa;(fa=f[x])!=tar;rotate(x))
        if (f[fa]!=tar)
            rotate((get(x)==get(fa))?fa:x);
    if (!tar) root=x;
}
int find(int x)
{
    int now=root;
    while (1)
    {
        if (ch[now][0]&&size[ch[now][0]]>=x) now=ch[now][0];
        else
        {
            if (ch[now][0]) x-=size[ch[now][0]];
            if (x==1) return now;
            --x;
            now=ch[now][1];
        }
    }
}
bool check(int mid)
{
    // [x,x+mid-1]
    int aa=find(x+mid+1);
    splay(px,0);
    splay(aa,px);
    UL hx=hash[ch[ch[root][1]][0]];
    // [y,y+mid-1]
    int bb=find(y+mid+1);
    splay(py,0);
    splay(bb,py);
    UL hy=hash[ch[ch[root][1]][0]];
    return hx==hy;
}
int dvd()
{
    int l=1,r=n-y+1,mid,ans=0;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
int main()
{
    mi[0]=1;for (int i=1;i<=100000;++i) mi[i]=mi[i-1]*S;
    scanf("%s",s+1);n=strlen(s+1);
    s[0]=s[n+1]='$';
    root=build(1,n+2,0);
    scanf("%d",&m);
    for (int i=1;i<=m;++i)
    {
        char opt=getchar();
        while (opt!='Q'&&opt!='R'&&opt!='I') opt=getchar();
        if (opt=='Q')
        {
            scanf("%d%d",&x,&y);
            if (x>y) swap(x,y);
            // x y
            px=find(x);
            py=find(y);
            ans=dvd();
            printf("%d\n",ans);
        }
        else if (opt=='R')
        {
            int x;char d;
            scanf("%d %c",&x,&d);
            // x
            int aa=find(x+1);
            splay(aa,0);
            key[root]=d;
            update(root);
        }
        else if (opt=='I')
        {
            int x;char d;
            scanf("%d %c",&x,&d);
            // [x,x+1]
            int aa=find(x+1);
            int bb=find(x+2);
            splay(aa,0);
            splay(bb,aa);
            ++n;
            key[n+2]=hash[n+2]=d;size[n+2]=1;
            f[n+2]=ch[root][1];ch[ch[root][1]][0]=n+2;
            update(ch[root][1]);
            update(root);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值