zoukankan      html  css  js  c++  java
  • [BZOJ1014][JSOI2008]火星人prefix

    1014: [JSOI2008]火星人prefix

    Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 7820  Solved: 2487 [Submit][Status][Discuss]

    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个数据,没有插入操作。

    对于没有插入的情况,用$O(L)$的时间预处理hash值,然后二分长度,再用$O(1)$的时间判断是否存在,单次询问的时间复杂度为$O(logL)$

    加入插入操作之后,可以用Splay维护区间hash值,然后类似上面,只是需要额外的$O(logL)$的时间提取出区间,单次询问的时间复杂度为$O(log^{2}L)$

    #include <cstdio>
    #include <cstring>
    const int maxn = 110000 + 10;
    typedef unsigned long long ll;
    const ll seed = 131;
    char val[maxn];
    int tot, root, fa[maxn], son[maxn][2], siz[maxn], cnt = 0;
    ll hash[maxn], pow[maxn];
    inline void PushUp(int rt){
        siz[rt] = siz[son[rt][0]] + siz[son[rt][1]] + 1;
        hash[rt] = hash[son[rt][0]] + val[rt] * pow[siz[son[rt][0]]] + hash[son[rt][1]] * pow[siz[son[rt][0]] + 1];
    }
    inline void Rotate(int x){
        int f = fa[x], p = son[f][0] == x;
        son[f][!p] = son[x][p];
        fa[son[x][p]] = f;
        fa[x] = fa[f];
        if(fa[x]) son[fa[x]][son[fa[x]][1] == f] = x;
        son[x][p] = f;
        fa[f] = x;
        PushUp(f);
    }
    inline void Splay(int x, int t){
        int y, z;
        while(fa[x] != t){
            if(fa[fa[x]] == t) Rotate(x);
            else{
                y = fa[x];
                z = fa[y];
                if(son[y][1] == x ^ son[z][1] == y) Rotate(x);
                else Rotate(y);
                Rotate(x);
            }
        }
        PushUp(x);
        if(!t) root = x;
    }
    inline void Find(int kth, int t){
        int x = root, tmp;
        while(x){
            tmp = siz[son[x][0]];
            if(kth == tmp + 1){
                Splay(x, t);
                return;
            }
            else if(kth < tmp + 1) x = son[x][0];
            else{
                x = son[x][1];
                kth -= tmp + 1;
            }
        }
    }
    char s[maxn];
    int Insert(int st, int en){
        if(st > en) return 0;
        int mid = st + en >> 1, now = ++cnt;
        val[now] = s[mid];
        son[now][0] = Insert(st, mid - 1);
        if(son[now][0]) fa[son[now][0]] = now;
        son[now][1] = Insert(mid + 1, en);
        if(son[now][1]) fa[son[now][1]] = now;
        PushUp(now);
        return now;
    }
    inline void LCQ(){
        int x, y;
        scanf("%d %d", &x, &y);
        int l = 1, r = tot, mid, ret = 0;
        ll tmp;
        while(l <= r){
            mid = l + r >> 1;
            if(y + mid - 1 > tot)    r = mid - 1;
            else{
                Find(x, 0);
                Find(x + mid + 1, root);
                tmp = hash[son[son[root][1]][0]];
                Find(y, 0);
                Find(y + mid + 1, root);
                if(tmp == hash[son[son[root][1]][0]]){
                    ret = mid;
                    l = mid + 1;
                }
                else r = mid - 1;
            }
        }
        printf("%d
    ", ret);
    }
    int main(){
        pow[0] = 1;
        for(int i = 1; i < maxn; i++)
            pow[i] = pow[i - 1] * seed;
        siz[0] = hash[0] = fa[0] = son[0][0] = son[0][1] = 0;
        scanf("%s", s + 1);
        tot = strlen(s + 1);
        root = Insert(0, tot + 1);
        int m;
        scanf("%d", &m);
        char opt[5], d[5];
        int x, y, t;
        while(m--){
            scanf("%s", opt);
            switch(opt[0]){
                case 'I':
                    scanf("%d%s", &x, d);
                    Find(x + 1, 0);
                    Find(x + 2, root);
                    cnt++;
                    val[cnt] = d[0];
                    fa[cnt] = son[root][1];
                    son[son[root][1]][0] = cnt;
                    PushUp(cnt);
                    PushUp(son[root][1]);
                    PushUp(root);
                    tot++;
                    break;
                case 'R':
                    scanf("%d%s", &x, d);
                    Find(x, 0);
                    Find(x + 2, root);
                    t = son[son[root][1]][0];
                    val[t] = d[0];
                    PushUp(t);
                    PushUp(son[root][1]);
                    PushUp(root);
                    break;
                case 'Q':
                    LCQ();
                    break;
            }
        }
        return 0;
    }
  • 相关阅读:
    System.Diagnostics.Process.Start()
    Asp.Net 构架(HttpModule 介绍) Part.3
    Asp.Net 构架(Http Handler 介绍) Part.2
    Asp.Net构架(Http请求处理流程)
    Ruby 2.0 发布首个预览版
    Java基本数据类型及类型转换
    J2EE 1.4 APIs and Technologies
    java final 关键字
    Android获取通讯录
    Activity的四种加载模式(转载)
  • 原文地址:https://www.cnblogs.com/ruoruoruo/p/7439925.html
Copyright © 2011-2022 走看看