uva11996Jewel Magic (1.二分 分块 hash)(2.splay)

I am a magician. I have a string of emeralds and pearls. I may insert new jewels in the string, or remove old ones. I may even reverse a consecutive part of the string. At anytime, if you point to two jewels and ask me, what is the length of the longest common prefix (LCP) of jewel strings starting from these two jewels, I can answer your question instantly. Can you do better than me? Formally, you’ll be given a string of 0 and 1. You’re to deal with four kinds of operations (in the following descriptions, L denotes the current length of the string, and jewel positions are number 1 to L numbered from left to right):

题意:

给定一个长度为n的01串,你的任务是依次执行如表所示的m条指令:
1 p c 在第p个字符后插入字符,p = 0表示在整个字符串之前插入
2 p 删除第p个字符,后面的字符往前移
3 p1 p2反转第p1到第p2个字符
4 p1 p2输出从p1开始和p2开始的两个后缀的LCP。

tip1:

时至今日,终于会了splay 以前一些不可做的题有了新的眼界,这道题还是前三个操作都正常的splay 4的问题可以二份答案之后计算hash,splay维护子树的正着hash和反着hash的值,和分块做法几乎是一样的

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define ls s[x][0]
#define rs s[x][1]
const int maxn = 400000+10;
int siz[maxn],s[maxn][2],tot,val[maxn],n,m,flip[maxn];
unsigned int Hash[maxn][2],p[maxn],mx = 7;
int root;

void push_down(int x){
    if(flip[x]){
        flip[x] = 0;
        if(ls!=-1){
            flip[ls] ^= 1;
            swap(s[ls][0],s[ls][1]);
            swap(Hash[ls][0],Hash[ls][1]);
        }
        if(rs!=-1){
            flip[rs] ^= 1;
            swap(s[rs][0],s[rs][1]);
            swap(Hash[rs][0],Hash[rs][1]);
        }
    }
}
int fd(int x,int k){//-1:根 1:右 0:左
    push_down(x); // !?
    if(ls == -1){
        if(k == 1) return -1;
        else return 1;
    }
    else{
        if(k <= siz[ls]) return 0;
        else if(k == siz[ls]+1) return -1;
        else return 1;
    }
}

void Push_up(int x){
    siz[x] = (ls==-1?0:siz[ls])+(rs==-1?0:siz[rs])+1;
    if(ls == -1 && rs == -1){
        Hash[x][0] = Hash[x][1] = val[x]; //!? "= 0"
    }
    else if(ls != -1 && rs != -1){
        int len0= siz[ls],len1 = siz[rs];
        Hash[x][0] = Hash[ls][0] + val[x] * p[len0] + Hash[rs][0] * p[len0+1];
        Hash[x][1] = Hash[rs][1] + val[x] * p[len1] + Hash[ls][1] * p[len1+1];
    }
    else if(ls == -1 && rs != -1){
        int len1 = siz[rs];
        Hash[x][0] = val[x] + Hash[rs][0] *p[1];
        Hash[x][1] = Hash[rs][1] + val[x] * p[len1];
    }
    else{
        int len0 = siz[ls];
        Hash[x][0] = Hash[ls][0] + val[x] * p[len0];
        Hash[x][1] = val[x] + Hash[ls][1] * p[1];
    }
}
void Rotate(int &x,int d){//d = 0:x右儿子变成跟,1:左变根
    push_down(x);push_down(s[x][d^1]);
    int k = s[x][d^1];s[x][d^1] = s[k][d];s[k][d] = x;
    Push_up(x); Push_up(k);
    x = k;
}
void Splay(int &x,int k){
    int d = fd(x,k);
    if(d == -1) return ;
    if(d == 1) k -= (ls==-1?0:siz[ls])+1;
    int p = s[x][d];
    int d2 = fd(p,k),k2;
    //注释以d = 0为例
    if(d2 == 0)         k2 = k;
    else if(d2 == 1)    k2 = k-(s[p][0]==-1?0:siz[ s[p][0] ])-1;
    if(d2 != -1){
        Splay(s[p][d2],k2);
        if(d == d2) Rotate(x,1-d);
        //两次方向一样,例如都是0(往左的链) 把根的第一个儿子(左边)转到根
        else        Rotate(s[x][d],1-d2);
        //两次方向不一样 例如左右,把第一个(根的左边)的右儿子(第二个)转到跟的右侧
    }
    Rotate(x,1-d);//跟的左侧转到跟
}
int new_node(int vl){
    s[tot][0] = s[tot][1] = -1;
    siz[tot] = 1;flip[tot] =0;
    val[tot] =  Hash[tot][0] = Hash[tot][1]  = vl;

    return tot++;
}
void Insert(int &x,int pos,int val){//for 1:ro2 1   pos pos-1
    Splay(x,pos-1);
    Splay(rs,pos-1-(ls==-1?0:siz[ls]));
    s[rs][0] = new_node(val);
    Push_up(rs);Push_up(x);
}
void pre(){
    tot = 0;root = 0;
    siz[tot++] = 2;siz[tot++] = 1;s[0][1] = tot-1;
    s[0][0] = s[1][0] = s[1][1] = -1;

    for(int i = 0 ; i <= 1; i++)
        val[i] =  Hash[i][0] = Hash[i][1] = flip[i] =  0;
    p[0] = 1;
    for(int i = 1; i < maxn ; i++)  p[i] = p[i-1]*mx;
}
void out(int x,int h){
    if(x == -1){
        return;
    }
    out(rs,h+1);
    cout<<"|";
    for(int i=0;i<h*6;i++) printf(" ");

    printf("%d:%d[flip:%d;siz=%d]: ",x,val[x],flip[x],siz[x]);
    for(int i=0;i<siz[x];i++) cout<<((Hash[x][0]>>i)&1); cout<<"\n";  //use it to output the string is mx=2

    out(ls,h+1);
}

void Del(int &x,int pos){
    Splay(x,pos-1);
    Splay(rs,pos+1-1-(ls==-1?0:siz[ls]));
    s[rs][0] = -1;
    Push_up(rs);
    Push_up(x);
}
unsigned int check(int &x,int L1,int R1){
    unsigned int xval;
    Splay(x,L1-1);
    Splay(rs,R1+1-1-(ls == -1?0:siz[ls]));
    xval = Hash[s[rs][0]][flip[s[rs][0]]]; // !? "Hash[s[rs][0]][flip[x]]"
    Push_up(rs);
    Push_up(x); // !?
    return xval;
}
int Qury(int &x,int l,int r){
    if(check(x,l,l)!=check(x,r,r)) return 0;
    int L=1, R=min(siz[x]-l,siz[x]-r);
    while(L<R){
        int mid = (L+R+1)/2;
        unsigned int t1 = check(x,l,l+mid-1);
        unsigned int t2 = check(x,r,r+mid-1);
        if(t1==t2) L=mid;
        else R=mid-1;
    }
    return L;
}
/*
void Rep(int &x,int pos,int vl){
    Splay(x,pos-1);
    Splay(rs,pos+1-1-(ls==-1?0:siz[ls]));
    val[s[rs][0]] = sum[s[rs][0]] = lsum[s[rs][0]] = rsum[s[rs][0]] = msum[s[rs][0]] = vl;
    Push_up(rs);Push_up(x);
}
*/
void Flip(int &x,int l,int r){
    Splay(x,l-1);Splay(rs,r+1-1-(ls==-1?0:siz[ls])); // !? "Splay(x,...) Splay(x,...)"
    int y=s[rs][0];
    flip[y] ^= 1;
    swap(Hash[y][0],Hash[y][1]); // !? "swap(Hash[x][0],Hash[x][1])"
    swap(s[y][0],s[y][1]);
    Push_up(rs);Push_up(x);
}
char op[3];
int _pos,_add,l,r,vl;
char S[maxn];
void init(){//插到第二个上
    scanf("%s",S);
    for(int i = 1; i <= n ; i++){
        Insert(root,i+1,S[i-1]-'0');
    }
   // out(root,0);
}
void sov(){
    //out(root,0);
    //cout<<endl;

    for(int i =1; i <= m ; i++){
        scanf("%s",op);
        if(op[0] == '1'){
            scanf("%d%d",&_pos,&_add);
            Insert(root,_pos+2,_add);//变成整个序列的第pos+2个
        }
        if(op[0] == '2'){
            scanf("%d",&_pos);
            Del(root,_pos+1);//删除整个序列的第pos+1个
        }
        if(op[0] == '4'){
            scanf("%d%d",&l,&r);
            cout << Qury(root,l+1,r+1) <<endl;
        }
        if(op[0] == '3'){
            scanf("%d%d",&l,&r);
            Flip(root,l+1,r+1);
        }

        //out(root,0);
        //cout<<endl;
    }
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    while(2==scanf("%d%d",&n,&m)){
        pre();
        init();
        sov();
    }
}
/*
12 9
000100001100
1 0 1
4 2 6
3 7 10
4 1 7
2 9
4 3 11
4 1 9
4 1 7
4 2 3
*/

tip2:

分块,根号(n+m)/2 ~根号(n+m)的长度,维护该块index,left块,right块,里面的字符串,字符串长度,正反hash值。
1.插入,从第一个块开始(首尾添加块0)算长度,找到属于的块,插入,若lenth>根号(n+m) ,把一个块分开,暴力后面块,前面可以直接球出来
若合格,O(lenth)的复杂度重新算hash
2.删除,若太小,则合并,合并两个块只需要O(1), 否则直接删除。。。删除时候O(lenth)
3.反转:若干整块加两边的小块,小块的地方直接暴力,求两个块新的hash值,中间部分直接反转,正反hash掉,然后left块,right块反过来O(lenth)
4.询问,二分当前lenth和,看这么长的字符串hash是否相同,O(1)求多个块合起来的hash 零碎部分暴力O(lenth*logn)

上述复杂度的lenth都是根号n的规模

因为保存了字符串,但是反转的时候并不会动,而是开is_roll记录一下(异或),在每次用到这个区间内部的时候,在转回来

/*
题意:给定一个长度为n的01串,你的任务是依次执行如表所示的m条指令:
1 p c 在第p个字符后插入字符,p = 0表示在整个字符串之前插入
2 p   删除第p个字符,后面的字符往前移
3 p1 p2反转第p1到第p2个字符
4 p1 p2输出从p1开始和p2开始的两个后缀的LCP。
*/
/*
分块,根号(n+m)/2 ~根号(n+m)的长度,维护该块index,left块,right块,里面的字符串,字符串长度,正反hash值。
1.插入,从第一个块开始(首尾添加块0)算长度,找到属于的块,插入,若lenth>根号(n+m) ,把一个块分开,暴力后面块,前面可以直接球出来
若合格,O(lenth)的复杂度重新算hash
2.删除,若太小,则合并,合并两个块只需要O(1), 否则直接删除。。。删除时候O(lenth)
3.反转:若干整块加两边的小块,小块的地方直接暴力,求两个块新的hash值,中间部分直接反转,正反hash掉,然后left块,right块反过来O(lenth)
4.询问,二分当前lenth和,看这么长的字符串hash是否相同,O(1)求多个块合起来的hash 零碎部分暴力O(lenth*logn)

上述复杂度的lenth都是根号n的规模
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 3000;
const int maxm = 4e6+10;
typedef unsigned long long ui;
ui P = 3,P_power[maxn];
struct node{
    int index,left,right,lenth;
    int is_r;
    ui f_hash,b_hash;
    char s[maxn];
     void roll(){
        if(is_r == 1){
            is_r = 0;
            for(int i = 0 ,j = lenth-1; i <= j; i++,j--)
                swap(s[i],s[j]);
        }
    }
    void print(){
        //roll();
        if(is_r == 0)
        for(int i = 0; i < lenth ; i++) cout <<s[i];
        else
            for(int i = lenth-1; i >= 0 ; i--)  cout <<s[i];
        cout <<endl;
        printf("block[%d].left = %d  right = % d  lenth = %d  f_hash = %d  b_hash = %d\n",index,left,right,lenth,f_hash,b_hash);
    }
    ui getb_hash(){
        ui tmp = 0;
        for(int i = lenth-1 ;i >= 0 ; i--)
            tmp = tmp *P+s[i]-'0'+1;
        return tmp;
    }
    ui getf_hash(){
        ui tmp = 0;
        for(int i = 0 ;i <lenth ; i++)
            tmp = tmp *P+s[i]-'0'+1;
        return tmp;
    }
}block[maxn];

int n,m,low_size,up_size,num_block,cp,pos,num,p1,p2,tot;
char ini[maxm],c[maxn],d[maxn];

void init(){
    scanf("%s",ini);
    up_size = sqrt(n+m);low_size = up_size/2;
    //printf("up _ size = %d   low = %d\n",up_size,low_size);
    block[0].right = 1;num_block = n/low_size;
    int last_size = n-num_block*low_size+low_size;
    int pos = 0;
    //1~倒数第二块
    for(int i = 1; i < num_block ; i++){
        ui tmp = 0,bk = 0;
        block[i].lenth = low_size;block[i].left = i-1;block[i].right = i+1;block[i].index = i;
        for(int j = 0 ;pos < n && j < block[i].lenth;j++, pos++){
            block[i].s[j] = ini[pos];
            tmp = tmp*P+ini[pos]-'0'+1;
        }
        for(int j = block[i].lenth-1; j >= 0; j--)
            bk = bk*P+block[i].s[j]-'0'+1;
        block[i].f_hash = tmp;block[i].b_hash = bk;
        //cout <<"hahahah  "<<block[i].b_hash<<endl;
        block[i].is_r = 0;
    }
    //最后一块
    block[num_block].left = num_block-1;block[num_block].right = maxn-1;block[maxn-1].left = num_block;block[maxn-1].index = maxn-1;
    block[num_block].lenth = last_size;block[num_block].index = num_block;block[num_block].is_r = 0;
    for(int j = 0;pos < n ;j++,pos++){
       block[num_block].s[j] = ini[pos];
    }
    block[num_block].f_hash = block[num_block].f_hash;block[num_block].b_hash = block[num_block].b_hash;
}

void separate(int s_wi){
    ++num_block;
    int l1 = block[s_wi].lenth/2;
    int l2 = block[s_wi].lenth-l1;
    block[num_block].is_r = 0;
    for(int i = l1 ,j = 0; i < block[s_wi].lenth ; i++,j++)   block[num_block].s[j] = block[s_wi].s[i];

    block[num_block].index = num_block;
    block[num_block].left = s_wi;block[num_block].right = block[s_wi].right;block[num_block].lenth = l2;
    block[block[s_wi].right].left = num_block;block[s_wi].right = num_block;block[s_wi].lenth = l1;
    int c1 = s_wi,c2 = num_block;
    block[c1].f_hash = block[c1].getf_hash();
    block[c2].f_hash = block[c2].getf_hash();
    block[c1].b_hash = block[c1].getb_hash();
    block[c2].b_hash = block[c2].getb_hash();
}

void cp1(int pos,int add){
    if(pos == 0){
        block[block[0].right].roll();
        for(int i = block[block[0].right].lenth; i >= 1 ; i--)
            block[block[0].right].s[i] = block[block[0].right].s[i-1];
        block[block[0].right].s[0] = add+'0';
        block[block[0].right].lenth++;
        int c1 = block[0].right;
        block[c1].f_hash = block[c1].getf_hash();
        if(block[block[0].right].lenth > up_size){
            separate(block[0].right);
        }
        return;
    }

    int i,cl = block[0].right;

    while(pos > block[cl].lenth){
        pos-=block[cl].lenth;
        cl = block[cl].right;
    }
    block[cl].roll();
    block[cl].lenth++;
    for(int i = block[cl].lenth; i > pos ; i--)
        block[cl].s[i] = block[cl].s[i-1];
    block[cl].s[pos] = add+'0';
    block[cl].f_hash = block[cl].getf_hash();
    block[cl].b_hash = block[cl].getb_hash();
    if(block[cl].lenth > up_size)   separate(cl);
}
ui get_hash(int len,int p1){
    int c1 = block[0].right;
    int c3,p3 = len+p1-1;

    while(p1 > block[c1].lenth){
        p1 -= block[c1].lenth;
        p3 -= block[c1].lenth;
        c1 = block[c1].right;
    }
    block[c1].roll();
    p1--;//c1块的第p1位置(下标)开始
    c3 = c1;
    while(p3 > block[c3].lenth){
        p3 -= block[c3].lenth;
        c3 = block[c3].right;
    }
    block[c3].roll();
    ui tmp = 0,tp = 0;
    if(c1 == c3){//同一个快
        for(int i = p1 ; i < p3; i++)
            tmp = tmp*P+block[c1].s[i]-'0'+1;
    }
    else{
        for(int i = p1 ;i < block[c1].lenth; i++)   tmp = tmp*P+block[c1].s[i]-'0'+1;
        for(int i = c1 ; i != block[c3].left ;i = block[i].right)   tmp = tmp*P_power[block[block[i].right].lenth]+block[block[i].right].f_hash;
        for(int i = 0 ; i < p3; i++)   tp = tp*P+block[c3].s[i]-'0'+1;
        tmp = tmp*P_power[p3]+tp;
    }
    return tmp;
}

bool se_check(int len,int p1,int p2){
    ui k1 = get_hash(len,p1);
    ui k2 = get_hash(len,p2);
    if(k1 == k2)    return true;
    else    return false;
}

int cp4(int p1,int p2){
    int l = 1, r = tot - p2+1,ans = 0;
    while(l <= r){
        int mid = (l+r)/2;
        if(se_check(mid,p1,p2)){
            ans = max(ans,mid);
            l = mid+1;
        }
        else    r = mid-1;
    }
    return ans;
}

void check(){
    for(int i = block[0].right ; i != maxn-1 ; i= block[i].right){
        block[i].print();
    }
}

void _union(int cl){
    int oth = block[cl].right;
    block[block[oth].right].left = cl;
    block[cl].right = block[oth].right;
    for(int i = block[cl].lenth ,j = 0; j < block[oth].lenth ;i++,j++)
        block[cl].s[i] = block[oth].s[j];
    block[cl].lenth = block[cl].lenth+block[oth].lenth;
    block[cl].f_hash = block[cl].getf_hash();
    block[cl].b_hash = block[cl].getb_hash();
    if(block[cl].lenth > up_size)   separate(cl);
}

void cp2(int pos){
    int cl = block[0].right;

    while(pos > block[cl].lenth){
        pos -= block[cl].lenth;
        cl = block[cl].right;
    }
    block[cl].roll();
    for(int i = pos-1 ;i < block[cl].lenth-1 ; i++){
        block[cl].s[i] = block[cl].s[i+1];
    }
    block[cl].lenth--;
    block[cl].f_hash = block[cl].getf_hash();
    block[cl].b_hash = block[cl].getb_hash();

    if( block[cl].lenth < low_size)  {
        if(block[cl].left == 0 && block[cl].right == maxn-1)    return;
        if(block[cl].right == maxn-1 ){
            block[block[cl].left].roll();
            _union(block[cl].left);
        }
        else {
            block[block[cl].right].roll();
            _union(cl);
        }
    }
}

void cp3(int p1,int p2){
    //check();
    int c1 = block[0].right;
    while(p1 > block[c1].lenth){
        p1 -= block[c1].lenth;
        c1 = block[c1].right;
    }
    block[c1].roll();
    //block[c1].print();
    int c2 = block[0].right;
    while(p2 > block[c2].lenth){
        p2 -= block[c2].lenth;
        c2 = block[c2].right;
    }
    block[c2].roll();
   // block[c2].print();

    if(c1 == c2){
        for(int i = p1-1 ,j = p2-1; i <= j ; i++,j--){
            swap(block[c1].s[i],block[c1].s[j]);
        }
        block[c1].f_hash = block[c1].getf_hash();
        block[c1].b_hash = block[c1].getb_hash();
        return;
    }

    for(int i = block[c1].right ; i != c2; ){
        int j= block[i].right;
        swap(block[i].right,block[i].left);
        swap(block[i].f_hash,block[i].b_hash);
        block[i].is_r ^= 1;
        i = j;
       // cout <"in";
    }
    if(block[c1].right != c2){
        swap(block[c1].right , block[c2].left);
        block[block[c2].left].right = c2;
        block[block[c1].right].left = c1;
    }
    int lenc = 0,lend = 0;

    for(int i = 0 ; i < p1-1;i++)
        c[lenc++] = block[c1].s[i];
    for(int i = p2-1 ; i >= 0 ; i--)
        c[lenc++] = block[c2].s[i];

    for(int i = block[c1].lenth-1; i >= p1-1 ; i--)
        d[lend++] = block[c1].s[i];
    for(int i = p2; i < block[c2].lenth ; i++)
        d[lend++] = block[c2].s[i];
    for(int i = 0 ; i < lenc ; i++){
        //cout <<c[i];
        block[c1].s[i] = c[i];
    }
   // cout <<endl;
    for(int i = 0 ; i < lend ; i++){
        block[c2].s[i] = d[i];
       // cout <<d[i];
    }
   // cout <<endl;
    block[c1].lenth = lenc;block[c2].lenth = lend;
    block[c1].f_hash = block[c1].getf_hash();
    block[c2].f_hash = block[c2].getf_hash();
    block[c1].b_hash = block[c1].getb_hash();
    block[c2].b_hash = block[c2].getb_hash();
   // cout <<"len1 = "<<lenc <<"  lenb = "<<lend<<endl;
    if(lenc > up_size)  separate(c1);
    if(lend > up_size)  separate(c2);
    if(lenc < low_size){
        int cl = c1;
        //block[cl].print();
        if(block[cl].left == 0 && block[cl].right == maxn-1)    return;

        if(block[cl].right == maxn-1 ){
            block[block[cl].left].roll();
            _union(block[cl].left);
        }
        else {
            block[block[cl].right].roll();
            _union(cl);
        }
    }
    if(lend <  low_size){
        int cl = c2;
        if(block[cl].left == 0 && block[cl].right == maxn-1)    return;
        if(block[cl].right == maxn-1 ){
            block[block[cl].left].roll();
            _union(block[cl].left);
        }
        else {
            block[block[cl].right].roll();
            _union(cl);
        }
    }
}

void sov(){
    tot = n;
    for(int ca = 1; ca <= m ; ca++){
        scanf("%d",&cp);
        if(cp == 1){
            tot++;
            scanf("%d%d",&pos,&num);
            cp1(pos,num);
            //check();
        }
        if(cp == 2){
            tot--;
            scanf("%d",&pos);
            cp2(pos);
            //check();
        }
        if(cp == 3){
            scanf("%d%d",&p1,&p2);
            cp3(p1,p2);

            //block[2].print();
            //check();
        }
        if(cp == 4){
            scanf("%d%d",&p1,&p2);
           //    check();
            printf("%d\n",cp4(p1,p2));
        }
    }
}

int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    P_power[0] = 1;
    for(int i = 1; i < maxn-1 ; i++)
        P_power[i] = P_power[i-1]*P;
    scanf("%d%d",&n,&m);
    init();
    sov();
}
/*
12 100
000100001100
1 0 1
4 2 4
1 1 1
4 3 4
1 1 0
4 2 8
1 3 0
4 3 5
1 7 1
4 6 9
1 8 0
4 1 8
2 8
4 6 9
2 7
4 3 5
2 3
4 2 8
2 1
4 3 4
3 2 9
3 1 3
4 1 7
3 1 2
4 1 8
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值