zoukankan      html  css  js  c++  java
  • BZOJ 1014: [JSOI2008]火星人prefix

    传送门

    如果没有修改的操作,很容易想到 后缀数组 倍增+哈希求 LCQ

    如果有修改呢,哈希值就会发生改变,这时我们就要找一种数据结构来维护哈希值

    emm...改字符和插入字符....

    显然可以用平衡树维护

    所以总体思路就是用平衡树维护哈希值,然后倍增+哈希求LCQ

    怎么维护哈希值很容易想到,直接看具体代码好了

    inline void pushup(int x)
    {
        int &l=ch[x][0],&r=ch[x][1];
        sz[x]=sz[l]+sz[r]+1;
        t[x]=t[r]+pw[sz[r]]*val[x]+t[l]*pw[sz[r]+1];//t存哈希值
        //pw存底数的倍数
    }
    平衡树更新节点操作

    提取一段区间的哈希值也不难,把左边界节点-1旋到根,右边界节点+1旋到根的右儿子

    那么哈希值就是根的右儿子的左儿子的哈希值

    哈希直接用自然溢出哈希就好了,(据说BZOJ卡常数?)

    这题开始我是想二分写的,然后发现很多细节要搞

    最后改成倍增就好写了

    其他都是平衡树的基本操作了

    因为我们可能会访问到1节点-1和n节点+1,所以要多插入两个虚节点 0 号和 n+1 号

    具体实现时一定要记得有多两个节点

    一开始直接建一颗标准的平衡树就好了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef unsigned long long ull;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7,base=233;
    ull pw[N],t[N];//自然溢出
    int sz[N],fa[N],ch[N][2],val[N],rt,cnt;
    inline void pushup(int x)//维护哈希值和子树大小
    {
        int &l=ch[x][0],&r=ch[x][1];
        sz[x]=sz[l]+sz[r]+1;
        t[x]=t[r]+pw[sz[r]]*val[x]+t[l]*pw[sz[r]+1];
    }
    inline void rotate(int x,int &k)
    {
        int y=fa[x],z=fa[y],d=(ch[y][1]==x);
        if(y!=k) ch[z][(ch[z][1])==y]=x;
        else k=x;
        fa[x]=z; fa[y]=x; fa[ch[x][d^1]]=y;
        ch[y][d]=ch[x][d^1]; ch[x][d^1]=y;
        pushup(y); pushup(x);
    }
    inline void Splay(int x,int &k)
    {
        while(x!=k)
        {
            int y=fa[x],z=fa[y];
            if(y!=k)
            {
                if(ch[y][1]==x ^ ch[z][1]==y) rotate(x,k);
                else rotate(y,k);
            }
            rotate(x,k);
        }
    }
    inline int find(int k)//找到区间中第k名的节点并返回它的编号
    {
        int now=rt;
        while(1)
        {
            if(ch[now][0]&&k<=sz[ch[now][0]]) { now=ch[now][0]; continue; }
            if(k>sz[ ch[now][0] ]+1) k-=sz[ ch[now][0] ]+1,now=ch[now][1];
            else return now;
        }
    }
    inline ull get_hash(int l,int r)//从树中取出闭区间[l,r]哈希值
    {
        int x=find(r+1);
        Splay(find(l-1),rt); Splay(x,ch[rt][1]);
        return t[ch[x][0]];
    }
    inline int Q(int x,int y)//倍增求LCQ
    {
        int res=0; if(x<y) swap(x,y);
        for(int i=17;i>=0;i--)
        {
            if(x+(1<<i)-1>=sz[rt]) continue;//注意-1和>=,>=是因为有虚节点在最后
            if( get_hash(x,x+(1<<i)-1)!=get_hash(y,y+(1<<i)-1) ) continue;//注意减1,闭区间
            res|=(1<<i); x+=1<<i; y+=1<<i;//此处不用减1
        }
        return res;
    }
    inline void change(int k,int c)//修改操作
    {
        int x=find(k);//找到位置
        val[x]=c; Splay(x,rt);//修改后记得Splay一波来调整整颗树的形态和数据
    }
    inline void ins(int k,int c)//插入操作
    {
        int x=find(k);//先找到第k的节点
        if(!ch[x][1]) ch[x][1]=++cnt;//注意特判
        else//找它右边子树内最左的节点x,插入的位置就是x的左儿子
        {
            x=ch[x][1]; while(ch[x][0]) x=ch[x][0];
            ch[x][0]=++cnt;
        }
        fa[cnt]=x; val[cnt]=c; sz[cnt]=1;
        Splay(cnt,rt);//记得Splay一波来调整整颗树的形态和数据
    }
    inline void build(int l,int r,int f)//一开始先建一颗标准平衡树
    {
        int mid=l+r>>1; fa[mid]=f; ch[f][mid>f]=mid;
        if(mid>l) build(l,mid-1,mid);
        if(mid<r) build(mid+1,r,mid);
        pushup(mid);
    }
    int n,m;
    char s[N];
    int main()
    {
        scanf("%s",s); cnt=n=strlen(s)+2;//注意此时cnt=n
        m=read();
        pw[0]=1; for(int i=1;i<N;i++) pw[i]=pw[i-1]*base;//初始化pw
        for(int i=2;i<n;i++) val[i]=s[i-2]-'a',sz[i]=1;
        sz[1]=sz[n]=1; val[1]=val[n]=27;//两个虚节点
        rt=1+n>>1; build(1,n,0);
        char ss[5],c[5]; int a,b;
        while(m--)
        {
            scanf("%s",ss);
            if(ss[0]=='Q')
            {
                a=read(); b=read();
                printf("%d
    ",Q(a+1,b+1));//记得+1
                continue;
            }
            a=read(); scanf("%s",c);
            if(ss[0]=='R') change(a+1,c[0]-'a');//a+1
            else ins(a+1,c[0]-'a');//a+1
        }
        return 0;
    }
  • 相关阅读:
    JS随笔
    tp5 redis 单例模式 转载
    分享我编程工作经历及对软件开发前景的看法
    redis详解(一)-- 概述
    redis详解(二)-- 数据类型详解
    redis详解(四)-- 高可用分布式集群
    redis详解(三)
    新工科平台
    关于Nginx的负载均衡
    微信退款回调
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10113683.html
Copyright © 2011-2022 走看看