UVA 11996 Jewel Magic(splay)

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3147

题意:给出一个初始长度为n的01串,四种操作:(1)在位置x插入y;(2)删除位置x的内容;(3)翻转区间[L,R]的内容;(4)输出从x开始和从y开始的串的最长公共前缀。

思路:(1)首先,插入、删除和翻转都是基本的splay操作,不再多说;(2)新颖的地方在于这里要查找两个串的最长公共前缀。首先二分这个长度,然后splay中利用哈希记录每个子树的串。然后比如对于查询从x开始长度为len的串的哈希值,只要将x-1和x+len分别调整到根和根的右子树,则根的左子树就是这个串,然后就可以得到其哈希值。值得注意的是,由于有翻转操作,我们保存两个哈希值,一个正的一个反的,翻转时交换就行。

 
struct node
{
    int value,rev,size;
    u64 h[2];
    node* c[2];
    node* p;
};


node a[N],*root,*null;
int tot;




u64 Pow[N];


node* newNode(int value,node *p)
{
    node *e=&a[++tot];
    e->value=value;
    e->size=1;
    e->p=p;
    e->rev=0;
    e->c[0]=e->c[1]=null;
    e->h[0]=e->h[1]=value;
    return e;
}






void pushDown(node *p)
{
    if(p==null) return;
    if(p->rev)
    {
        p->rev=0;
        swap(p->c[0],p->c[1]);
        swap(p->h[0],p->h[1]);
        p->c[0]->rev^=1;
        p->c[1]->rev^=1;
    }
}






void pushUp(node *p)
{
    if(p==null) return;
    p->size=p->c[0]->size+p->c[1]->size+1;
    p->h[0]=p->c[0]->h[0]*Pow[1+p->c[1]->size]+p->value*Pow[p->c[1]->size]+p->c[1]->h[0];
    p->h[1]=p->c[1]->h[1]*Pow[1+p->c[0]->size]+p->value*Pow[p->c[0]->size]+p->c[0]->h[1];
}


void init()
{
    tot=0;
    null=0;
    null=newNode(0,0);
    null->size=0;
    root=newNode(0,null);
    root->c[1]=newNode(0,root);
    pushUp(root);
}


void rotate(node *x,int k)
{
    node *y=x->p;
    pushDown(x->c[0]);
    pushDown(x->c[1]);
    pushDown(y->c[!k]);
    y->c[k]=x->c[!k];
    y->c[k]->p=y;
    x->p=y->p;
    if(y->p->c[0]==y) y->p->c[0]=x;
    else y->p->c[1]=x;
    y->p=x;
    x->c[!k]=y;
    pushUp(y);
    pushUp(x);
    if(root==y) root=x;
}






void splay(node *x,node *y)
{
    pushDown(x);
    while(x->p!=y)
    {
        if(x->p->p==y)
        {
            if(x->p->c[0]==x) rotate(x,0);
            else rotate(x,1);
        }
        else if(x->p->p->c[0]==x->p)
        {
            if(x->p->c[0]==x) rotate(x->p,0);
            else rotate(x,1);
            rotate(x,0);
        }
        else
        {
            if(x->p->c[1]==x) rotate(x->p,1);
            else rotate(x,0);
            rotate(x,1);
        }
    }
}




void select(int k,node *y)
{
    node *x=root;
    pushDown(x);
    while(k!=x->c[0]->size+1)
    {
        if(k<x->c[0]->size+1) x=x->c[0];
        else
        {
            k-=x->c[0]->size+1;
            x=x->c[1];
        }
        pushDown(x);
    }
    splay(x,y);
}


void build(int L,int R,int b[],node *p,int side)
{
    if(L>R) return;
    int mid=(L+R)>>1;
    node *x=newNode(b[mid],p);
    p->c[side]=x;
    build(L,mid-1,b,x,0);
    build(mid+1,R,b,x,1);
    pushUp(x);
}




void insert(int pos,int cnt,int b[])
{
    select(pos+1,null);
    select(pos+2,root);
    build(1,cnt,b,root->c[1],0);
    splay(root->c[1]->c[0],null);
}


void del(int pos,int cnt)
{
    select(pos,null);
    select(pos+cnt+1,root);
    root->c[1]->c[0]=null;
    splay(root->c[1],null);
}




void reverse(int pos,int cnt)
{
    select(pos,null);
    select(pos+cnt+1,root);
    root->c[1]->c[0]->rev^=1;
    splay(root->c[1]->c[0],null);
}






u64 cal(int x,int y)
{
    select(x,null);
    select(y+2,root);
    return root->c[1]->c[0]->h[0];
}


int b[N],n,m;


int query(int x,int y)
{
    int low=0,high=n-y+1,mid;
    while(low<=high)
    {
        mid=(low+high)>>1;
        if(cal(x,x+mid-1)==cal(y,y+mid-1)) low=mid+1;
        else high=mid-1;
    }
    if(low<=n-y+1&&cal(x,x+low-1)==cal(y,y+low-1)) return low;
    return high;
}






char str[N];


int main()
{
    int i;
    Pow[0]=1;
    FOR1(i,N-1) Pow[i]=Pow[i-1]*7;
    Rush(n)
    {
        RD(m); RD(str+1); init();
        FOR1(i,n) b[i]=str[i]-'0';
        insert(0,n,b);
        int x,y,op;
        while(m--)
        {
            RD(op);
            if(op==1) RD(x,b[1]),n++,insert(x,1,b);
            else if(op==2) RD(x),n--,del(x,1);
            else if(op==3) RD(x,y),reverse(x,y-x+1);
            else RD(x,y),printf("%d\n",query(x,y));
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值