1. 程式人生 > >BZOJ1014 JSOI2008 火星人prefix 【非旋轉Treap】*

BZOJ1014 JSOI2008 火星人prefix 【非旋轉Treap】*

BZOJ1014 JSOI2008 火星人prefix

Description

  火星人最近研究了一種操作:求一個字串兩個字尾的公共字首。比方說,有這樣一個字串:madamimadam,我們將這個字串的各個字元予以標號:序號: 1 2 3 4 5 6 7 8 9 10 11 字元 m a d a m i m a d a m 現在,火星人定義了一個函式LCQ(x, y),表示:該字串中第x個字元開始的字串,與該字串中第y個字元開始的字串,兩個字串的公共字首的長度。比方說,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函式的過程中,火星人發現了這樣的一個關聯:如果把該字串的所有後綴排好序,就可以很快地求出LCQ函式的值;同樣,如果求出了LCQ函式的值,也可以很快地將該字串的字尾排好序。 儘管火星人聰明地找到了求取LCQ函式的快速演算法,但不甘心認輸的地球人又給火星人出了個難題:在求取LCQ函式的同時,還可以改變字串本身。具體地說,可以更改字串中某一個字元的值,也可以在字串中的某一個位置插入一個字元。地球人想考驗一下,在如此複雜的問題中,火星人是否還能夠做到很快地求取LCQ函式的值。

Input

  第一行給出初始的字串。第二行是一個非負整數M,表示操作的個數。接下來的M行,每行描述一個操作。操作有3種,如下所示
1、詢問。語法:Qxy,x,y均為正整數。功能:計算LCQ(x,y)限制:1<=x,y<=當前字串長度。
2、修改。語法:Rxd,x是正整數,d是字元。功能:將字串中第x個數修改為字元d。限制:x不超過當前字串長度。
3、插入:語法:Ixd,x是非負整數,d是字元。功能:在字串第x個字元之後插入字元d,如果x=0,則在字串開頭插入。限制:x不超過當前字串長度

Output

  對於輸入檔案中每一個詢問操作,你都應該輸出對應的答案。一個答案一行。

Sample Input

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11

Sample Output

5
1
0
2
1

HINT

1、所有字串自始至終都只有小寫字母構成。
2、M<=150,000
3、字串長度L自始至終都滿足L<=100,000
4、詢問操作的個數不超過10,000個。
對於第1,2個數據,字串長度自始至終都不超過1,000
對於第3,4,5個數據,沒有插入操作。

我們發現有修改和新增字元的操作,就想到了平衡樹
又因為我比較喜歡非旋轉Treap
然後就寫了一波

注意這裡的Treap不能按照字元的大小來建樹,我們應該根據字串中字元的先後順序來建樹,新增字元就直接在第k個位置後面新增就好了,修改比較常規,直接先分裂再合併就行了

現在我們考慮怎麼求字串的兩個字尾的最長公共字首,首先如果沒有修改和新增操作我們肯定會選擇字尾陣列,但現在我們有了修改和新增操作,所以我們需要換一種方式思考。在一般情況下,字串的hash值是否相同可以判斷字串是否相同,所以我們可以用hash,但是如果這樣暴力判斷,時間效率依舊不優秀,達到O(n2logn),那麼我們可以發現這個hash段的長度是具有二分性的,所以我們就可以用二分來查詢,就可以將時間優化成O(nlog2n)

那麼我們怎麼維護hash呢,首先,在Treap上進行向上更新操作的時候可以通過合併左右區間hash值來維護,那麼我們就需要與處理base偏移量(pow陣列),然後通過
hash[t]=hash[ls[t]]+pow[siz[ls[t]]]val[t]+pow[siz[ls[t]]+1]hash[rs[t]]
來計算當前節點為根的子樹hash值,其中val是當前節點所代表的字元的編號(字元-‘a’+1)

然後我們在二分的時候直接剝離出從x、y開始長度為len的子樹就好了

#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define Base 37
typedef pair<int,int> pi;
struct Treap{
    int tot,root,n;
    int siz[N],key[N],ls[N],rs[N],val[N];
    unsigned int hash[N],pow[N];
    void init(){
        tot=root=0;
        pow[0]=1;
        for(int i=1;i<=N;i++)
            pow[i]=pow[i-1]*Base;
    }
    int new_treap_point(int vl){
        int newt=++tot;
        siz[newt]=1;
        hash[newt]=val[newt]=vl;
        key[newt]=rand();
        ls[newt]=rs[newt]=0;
        return newt;
    }
    void update(int t){
        siz[t]=siz[ls[t]]+siz[rs[t]]+1;
        hash[t]=hash[ls[t]]+pow[siz[ls[t]]]*val[t]+pow[siz[ls[t]]+1]*hash[rs[t]];
    }
    int merge(int a,int b){
        if(!a)return b;
        if(!b)return a;
        if(key[a]<key[b]){
            rs[a]=merge(rs[a],b);
            update(a);return a;
        }else{
            ls[b]=merge(a,ls[b]);
            update(b);return b;
        }
    }
    pi split(int t,int k){
        if(!t)return pi(0,0);
        pi ans;
        if(siz[ls[t]]>=k){
            pi tmp=split(ls[t],k);
            ls[t]=tmp.second;
            update(t);
            ans.first=tmp.first;
            ans.second=t;
        }else{
            pi tmp=split(rs[t],k-siz[ls[t]]-1);
            rs[t]=tmp.first;
            update(t);
            ans.first=t;
            ans.second=tmp.second;
        }
        return ans;
    }
    void insert(int k,int vl){
        pi x=split(root,k);
        int t=new_treap_point(vl);
        root=merge(merge(x.first,t),x.second);
    }
    void erase(int k){
        pi x=split(root,k);
        root=merge(split(x.first,k-1).first,x.second);
    }
    void modify(int k,int vl){
        erase(k);
        insert(k-1,vl);
    }
    int queryhash(int t,int len){
        if(t+len-1>siz[root])return -1;
        pi x=split(root,t-1);
        pi y=split(x.second,len);
        int ans=hash[y.first];
        root=merge(x.first,merge(y.first,y.second));
        return ans;
    }
    int query(int x,int y){
        int l=0,r=N,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            int px=queryhash(x,mid),py=queryhash(y,mid);
            if(px==-1||py==-1){r=mid-1;continue;}
            if(px==py)ans=mid,l=mid+1;
            else r=mid-1;
        }
        return ans;
    }
}treap;
int n,m;
char s[N],c[5];
int main(){
    srand(time(0));
    treap.init();
    scanf("%s",s+1);
    n=strlen(s+1);
    treap.n=n;
    for(int i=1;i<=n;i++)
        treap.root=treap.merge(treap.root,treap.new_treap_point(s[i]-'a'+1));
    scanf("%d",&m);
    while(m--){
        scanf("%s",c);
        if(c[0]=='Q'){
            int x,y;scanf("%d%d",&x,&y);
            printf("%d\n",treap.query(x,y));
        }else if(c[0]=='I'){
            int x;scanf("%d%s",&x,c);
            treap.insert(x,c[0]-'a'+1);
        }else{
            int x;scanf("%d%s",&x,c);
            treap.modify(x,c[0]-'a'+1);
        }
    }
    return 0;
}