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

bzoj1014 [JSOI2008]火星人prefix

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=1014

题意:
给定一个字符串,比方说,有这样一个字符串:madamimadam,
我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m
有3种操作:
1、询问。语法:Q x y,x, y均为正整数。
  功能:计算LCQ(x, y)
2、修改。语法:R x d,x是正整数,d是字符。
  功能:将字符串中第x个数修改为字符d。
  限制:x不超过当前字符串长度。
3、插入:语法:I x d,x是非负整数,d是字符。
  功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。
  限制:x不超过当前字符串长度。

第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。

数据范围
1、所有字符串自始至终都只有小写字母构成。
2、M<=150,000
3、字符串长度L自始至终都满足L<=100,000
4、询问操作的个数不超过10,000个。

题解:
复习一发spaly。

首先我们知道二分比较hash值可以log地得到两个串的最长公共前缀,而给出的替换和插入操作可以让我们想到splay维护。

于是我们用splay维护字符串,每个节点上存有它控制的区间的hash值。每次查询两个后缀的最长公共前缀,只需要二分长度,然后把这个区间转上去,重算hash值,比较这两个区间的hash值是否相同即可。

( wa点:size没搞对,二分的时候越界了。改回来时影响了关于查询的两个串是同一个串的情况,其实不用特判。)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL unsigned long long
using namespace std;
const int N=100010;
const int M=150010;
const LL base=131;
int root,tail=0,fa[N];
struct node
{
    int ch[2],size;
    LL val,key;
}tr[N];
int m,len;
LL mi[N];
char s[M],opt[5];
void update(int x)
{
    int ls=tr[x].ch[0]; int rs=tr[x].ch[1];
    tr[x].key=tr[x].val; tr[x].size=1;
    if(rs)
    {
        tr[x].size+=tr[rs].size;
        tr[x].key=tr[x].key*mi[tr[rs].size]+tr[rs].key;
    }
    if(ls)
    {
        tr[x].size+=tr[ls].size;
        tr[x].key=tr[ls].key*mi[tr[x].size-tr[ls].size]+tr[x].key;
    }
    return;
}
void build(int &nd,int lf,int rg,int f)
{
    if(lf>rg) return;
    nd=++tail; fa[nd]=f;
    tr[nd].ch[0]=tr[nd].ch[1]=0;
    if(lf==rg)
    {
        tr[nd].size=1;
        tr[nd].val=tr[nd].key=(int)s[lf];
        return;
    }
    int mid=(lf+rg)>>1;
    tr[nd].val=s[mid];
    build(tr[nd].ch[0],lf,mid-1,nd);
    build(tr[nd].ch[1],mid+1,rg,nd);
    update(nd);

}
int find(int nd,int pos)
{   
    int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];
    if(pos==tr[ls].size+1) return nd;
    else if(pos<=tr[ls].size) return find(tr[nd].ch[0],pos);
    else return find(tr[nd].ch[1],pos-tr[ls].size-1);
}
void rotate(int x,int &k)
{
    int y=fa[x]; int z=fa[y];
    int l,r;
    if(x==tr[y].ch[0]) l=0; else l=1; r=l^1;
    if(y==k) k=x;
    else if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;
    fa[x]=z;
    tr[y].ch[l]=tr[x].ch[r];
    fa[tr[x].ch[r]]=y;
    tr[x].ch[r]=y; fa[y]=x;
    update(y); update(x);
}
void splay(int x,int &k)
{
    while(x!=k)
    {
        int y=fa[x]; int z=fa[y];
        if(y!=k)
        {
            if((tr[y].ch[0]==x)^(tr[z].ch[0]==y))
            rotate(x,k);
            else rotate(y,k);
        }   
        rotate(x,k);        
    }
}
LL check(int l,int r)
{
    splay(l,root); splay(r,tr[l].ch[1]);
    int nd=tr[tr[l].ch[1]].ch[0];
    return tr[nd].key;
}
int query(int p,int q)      //询问x,y的lcp 
{

    int x=find(root,p); int y=find(root,q);
    if(tr[x].val!=tr[y].val) return 0;
    int lx,rx,ly,ry;
    lx=find(root,p-1);  ly=find(root,q-1);
    int sz=tr[root].size;
    int lf=1; int rg=min(sz-p,sz-q);
    while(lf+1<rg)
    {
        int mid=(lf+rg)>>1;
        rx=find(root,p+mid); 
        LL v1=check(lx,rx); 
        ry=find(root,q+mid);
        LL v2=check(ly,ry);
        if(v1==v2) lf=mid;
        else rg=mid;
    }
    int ans;
    rx=find(root,p+rg); 
    LL v1=check(lx,rx); 
    ry=find(root,q+rg);
    LL v2=check(ly,ry);
    if(v1==v2) ans=rg;
    else ans=lf;
    return ans;
}
int main()
{
    mi[0]=1;
    for(int i=1;i<N;i++)
    mi[i]=mi[i-1]*base;

    scanf("%s",s+2);
    len=strlen(s+2);
    tr[0].size=0;
    build(root,1,len+2,0);

    scanf("%d",&m);
    while(m--)
    {   
        scanf("%s",opt);
        if(opt[0]=='Q')
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x++; y++;
            printf("%d\n",query(x,y));
        }
        else if(opt[0]=='R')
        {
            int pos; char ccc[5];
            scanf("%d",&pos); scanf("%s",ccc);
            pos++;
            int c=(int)ccc[0];
            int nd=find(root,pos);
            splay(nd,root);
            tr[nd].val=c;
            update(nd);
        }
        else
        {
            int pos; char ccc[5];
            scanf("%d",&pos); scanf("%s",ccc);
            pos++;
            int c=(int)ccc[0];
            int f=find(root,pos);
            splay(f,root);
            int nd=++tail;
            tr[nd].val=c; 
            tr[nd].ch[1]=tr[f].ch[1]; fa[tr[nd].ch[1]]=nd;
            tr[f].ch[1]=nd;fa[nd]=f;
            tr[nd].ch[0]=0;
            update(nd); update(f);
        }
    }
    return 0;
}

专心,专心。

不理我算啦 (′へ` )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值