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

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

     

     

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

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

     

     

    正解:splay+二分答案+hash

    解题报告:

      这道题需要我们维护一个字符串,每次快速查询两个后缀的LCP,并且动态修改、往这个字符串插入元素。开始我一直在想用后缀数据结构,因为修改和插入操作不知道该怎么办,没有思路。后来发现插入和修改事实上可以用splay维护,而我的splay上的结点维护的就是这个结点控制的区间的,这一段的字符串的hash值。然后每次旋转的时候重新算一下就可以了。

      根据上述所说,插入操作,我可以旋转插入位置到根,下一位到根的右子树,然后直接往右子树的左子树上插就可以了。这样做的好处就是不用一路update,直接在根结点上修改。

      修改操作就更简单了,把修改的点旋转到根结点,然后修改、update即可。

      我们接下来考虑查询怎么做,因为需要查询两个后缀的LCP,我们并不能直接求出值,但是判断一下还是可以的,于是想到二分答案,然后splay旋转区间,比较hash值就可以了。

      这道题细节肯定没有维护数列多,不过我貌似常数写大了,虽然没TLE,但是跑的很慢我很不爽啊...于是就是一顿优化常数,比如把find改成非递归式,还有修改操作改成不旋转,找到之后一路往上update

      大概就是这样了。

      ps:结果BZOJ太慢了,后来改成了自然溢才过。

     

      1 //It is made by ljh2000
      2 #include <iostream>
      3 #include <cstdlib>
      4 #include <cstring>
      5 #include <cstdio>
      6 #include <cmath>
      7 #include <algorithm>
      8 #include <ctime>
      9 #include <vector>
     10 #include <queue>
     11 #include <map>
     12 #include <set>
     13 using namespace std;
     14 typedef unsigned long long LL;
     15 #define RG register
     16 const int inf = (1<<30);
     17 const int MAXN = 100011;
     18 //const int MOD = 1000007;
     19 int n,m,ans,tot,rt,ql,qr;
     20 char s[MAXN],ch[12];
     21 LL mi[MAXN],hash[MAXN];
     22 int tr[MAXN][2],size[MAXN],father[MAXN];
     23 
     24 inline int getint()
     25 {
     26     RG int w=0,q=0; RG char c=getchar();
     27     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
     28     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
     29 }
     30 
     31 inline void update(RG int x){
     32     RG int l=tr[x][0],r=tr[x][1]; size[x]=size[l]+size[r]+1;
     33     hash[x]=hash[r]+(s[x]-'a'+1)*mi[size[r]]; //hash[x]%=MOD;
     34     hash[x]+=hash[l]*mi[size[r]+1];// hash[x]%=MOD;
     35 }
     36 
     37 inline void rotate(RG int x,RG int &rt){
     38     RG int y=father[x],z=father[y];
     39     RG int l=(tr[y][1]==x),r=l^1;
     40     if(y==rt) rt=x; else tr[z][(tr[z][1]==y)]=x;
     41     father[x]=z; tr[y][l]=tr[x][r]; father[tr[x][r]]=y;
     42     father[y]=x; tr[x][r]=y;
     43     update(y); update(x);
     44 }
     45 
     46 inline void splay(RG int x,RG int &rt){
     47     RG int y,z;
     48     while(x!=rt) {
     49     y=father[x]; z=father[y];
     50     if(y!=rt) {
     51         if((tr[z][0]==y) ^ (tr[y][0]==x)) rotate(x,rt);
     52         else rotate(y,rt);
     53     }
     54     rotate(x,rt);
     55     }
     56 }
     57 
     58 inline void build(RG int l,RG int r,RG int fa){
     59     if(l>r) return ; RG int mid=(l+r)>>1;
     60     if(l==r) { hash[l]=s[l]-'a'+1; size[l]=1; }
     61     else build(l,mid-1,mid),build(mid+1,r,mid);
     62     tr[fa][mid>fa]=mid; father[mid]=fa; update(mid);
     63 }
     64 
     65 inline int rank(RG int root,RG int k){
     66     RG int l,r;
     67     while(1) {
     68     l=tr[root][0],r=tr[root][1];
     69     if(size[l]+1==k) return root;
     70     if(size[l]>=k) root=l; else root=r,k=k-size[l]-1;
     71     }
     72 }
     73 
     74 inline bool check(RG int len){
     75     RG int hash1; RG int x=rank(rt,ql); splay(x,rt);
     76     x=rank(rt,ql+len+1); splay(x,tr[rt][1]); x=tr[tr[rt][1]][0];
     77     hash1=hash[x];
     78     RG int hash2; x=rank(rt,qr); splay(x,rt);
     79     x=rank(rt,qr+len+1); splay(x,tr[rt][1]); x=tr[tr[rt][1]][0];
     80     hash2=hash[x];
     81     if(hash1==hash2) return true;
     82     return false;
     83 }
     84 
     85 inline void query(){
     86     ql=getint(),qr=getint(); ans=0;
     87     RG int l=1,r=tot-2-max(ql,qr)+1,mid;
     88     while(l<=r) {
     89     mid=(l+r)>>1;
     90     if(check(mid)) l=mid+1,ans=mid;
     91     else r=mid-1;
     92     }
     93     printf("%d
    ",ans);
     94 }
     95 
     96 inline void insert(){
     97     RG int pos=getint(); scanf("%s",ch); s[++tot]=ch[0]; hash[tot]=ch[0]-'a'+1;
     98     RG int x=rank(rt,pos+1); 
     99     splay(x,rt); x=rank(rt,pos+2); splay(x,tr[rt][1]);
    100     x=tr[rt][1]; father[tot]=x; size[tot]=1;
    101     tr[x][0]=tot; update(x); update(rt);
    102 }
    103 
    104 inline void change(){//只需要修改一条链,不用rotate
    105     RG int pos=getint(); scanf("%s",ch);
    106     RG int x=rank(rt,pos+1); splay(x,rt);
    107     s[x]=ch[0]; update(x);
    108 }
    109 
    110 inline void work(){
    111     scanf("%s",s+2); n=strlen(s+2);  m=getint(); 
    112     mi[0]=1; for(RG int i=1;i<=100010;i++) mi[i]=mi[i-1]*27;//,mi[i]%=MOD;
    113     s[1]=s[n+2]='a'; build(1,n+2,0); tot=n+2; rt=(1+n+2)/2;
    114     while(m--) {
    115     scanf("%s",ch);
    116     if(ch[0]=='Q') query();
    117     else if(ch[0]=='I') insert();
    118     else change();
    119     }
    120 }
    121 
    122 int main()
    123 {
    124     work();
    125     return 0;
    126 }
  • 相关阅读:
    centos 安装 TortoiseSVN svn 客户端
    linux 定时任务 日志记录
    centos6.5 安装PHP7.0支持nginx
    linux root 用户 定时任务添加
    composer 一些使用说明
    laravel cookie写入
    laravel composer 安装指定版本以及基本的配置
    mysql 删除重复记录语句
    linux php redis 扩展安装
    linux php 安装 memcache 扩展
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/5974875.html
Copyright © 2011-2022 走看看