bzoj1014 火星人prefix Splay 字符串Hash

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1014

题意:动态修改字符串,动态查询某两个后缀之间的$LCP$长度。

这题肯定一堆人刚开始:$woc$这题好水啊,后缀数据结构放肆撸啊……

等看到后半段:$woc$这个修改怎么改啊……

实际上这个东西跟后缀数据结构半毛钱关系没有……正确做法就是$Hash$……不停地插入字符,修改整棵子树的$Hash$值,然后就是动态的修改$Hash$值……转啊转……修改……转……然后就出来了……

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #define ls(x) ((x)->ch[0])
  6 #define rs(x) ((x)->ch[1])
  7 #define fa(x) (x->pa)
  8 using namespace std;
  9 const int base=2333,maxn=100005;
 10 unsigned long long tim[maxn];char s[maxn];
 11 struct node
 12 {
 13     int size,num;unsigned long long Has;
 14     node *ch[2],*pa;
 15     node(int has);
 16     int islc(){return ls(pa)==this;}
 17     void maintain();
 18 }*null=new node(0),*root=null;
 19 node::node(int has)
 20 {
 21     ch[0]=ch[1]=pa=null;
 22     num=Has=has;size=has?1:0;
 23 }
 24 void node::maintain()
 25 {
 26     size=ch[0]->size+ch[1]->size+1;
 27     Has=ch[0]->Has*tim[ch[1]->size+1]+num*tim[ch[1]->size]+ch[1]->Has;
 28 }
 29 void Rotate(node* rt,int d)
 30 {
 31     node *t=rt->ch[d^1];
 32     if(rt->pa!=null)rt->pa->ch[rt->islc()^1]=t;else root=t;
 33     t->pa=rt->pa;
 34     rt->ch[d^1]=t->ch[d];
 35     if(t->ch[d]!=null)t->ch[d]->pa=rt;
 36     t->ch[d]=rt,rt->pa=t;
 37     rt->maintain();t->maintain();
 38 }
 39 void Splay(node* x,node *tar)
 40 {
 41     for(node *rt=x->pa;rt!=tar;rt=x->pa)
 42     {
 43         if(rt->pa==tar){Rotate(rt,x->islc());break;}
 44         if(x->islc()==rt->islc())Rotate(rt->pa,x->islc());
 45         else Rotate(rt,x->islc());
 46         Rotate(x->pa,x->islc());
 47     }
 48     x->maintain();
 49 }
 50 void find(node *rt,int val,node *tar)
 51 {
 52     for(;;)
 53     {
 54         if(val<=ls(rt)->size)rt=ls(rt);
 55         else
 56         {
 57             val-=ls(rt)->size;
 58             if(val==1)break;
 59             val--,rt=rs(rt);
 60         }
 61     }
 62     Splay(rt,tar);
 63 }
 64 void build(node* &x,int l,int r)
 65 {
 66     if(l>r)return ;
 67     int mid=(l+r)>>1;x=new node(s[mid]-‘a‘+1);
 68     build(x->ch[0],l,mid-1),build(x->ch[1],mid+1,r);
 69     x->ch[0]->pa=x,x->ch[1]->pa=x;x->maintain();
 70 }
 71 void init()
 72 {
 73     tim[0]=1;
 74     for(int i=1;i<=100001;i++)tim[i]=tim[i-1]*base;
 75     scanf("%s",s);root=new node(20010117),root->ch[1]=new node(20010117);
 76     build(root->ch[1]->ch[0],0,strlen(s)-1);
 77     root->ch[1]->ch[0]->pa=root->ch[1],root->ch[1]->pa=root;root->ch[1]->maintain(),root->maintain();
 78 }
 79 bool check(int x,int y,int val)
 80 {
 81     find(root,x,null);find(root,x+val+1,root);
 82     unsigned long long Has1=root->ch[1]->ch[0]->Has;
 83     find(root,y,null);find(root,y+val+1,root);
 84     unsigned long long Has2=root->ch[1]->ch[0]->Has;
 85     return Has1==Has2;
 86 }
 87 int Query(int x,int y)
 88 {
 89     int l=0,r=root->size-1-max(x,y),mid;
 90     while(l<=r)
 91     {
 92         mid=(l+r)>>1;
 93         if(check(x,y,mid))l=mid+1;
 94         else r=mid-1;
 95     }
 96     return r;
 97 }
 98 int haha()
 99 {
100     init();char s[5];
101     int m;scanf("%d",&m);
102     while(m--)
103     {
104         scanf("%s",s);int x,y;
105         switch(s[0])
106         {
107             case ‘R‘:scanf("%d%s",&x,s);
108                     find(root,x+1,null);
109                     root->num=s[0]-‘a‘+1;root->maintain();break;
110             case ‘I‘:scanf("%d%s",&x,s);
111                     find(root,x+1,null);find(root,x+2,root);
112                     root->ch[1]->ch[0]=new node(s[0]-‘a‘+1),root->ch[1]->ch[0]->pa=root->ch[1];root->ch[1]->maintain();root->maintain();break;
113             default:scanf("%d%d",&x,&y);printf("%d\n",Query(x,y));
114         }
115     }
116 }
117 int sb=haha();
118 int main(){;}

bzoj1014

时间: 2024-08-08 02:04:37

bzoj1014 火星人prefix Splay 字符串Hash的相关文章

【BZOJ-1014】火星人prefix Splay + 二分 + Hash

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5852  Solved: 1871[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

BZOJ-1014 [JSOI2008]火星人prefix (Splay+二分+hash)

题解: 用splay维护添加修改操作,然后二分hash判断长度. 操作一:对于查询区间[l,r]的hash值,显然将l-1旋到根,将r+1旋到根的右儿子,此时所求区间就是根的右儿子的左儿子了. 操作二:将要修改的位置旋到根,然后直接改就可以了. 操作三:要在x后面添加一个字符,显然将x旋到根,x+1旋到根的右儿子,然后直接加在根的右儿子的左儿子上就可以了. 1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio&g

BZOJ 1014: [JSOI2008]火星人prefix( splay + hash )

用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 100009; const int P = 1000173169; ull K[maxn]; int N; char S[maxn]; struct Node { Node *ch[2], *p; int s, v; ull h; inline void

【bzoj1014】[JSOI2008]火星人prefix Splay+Hash+二分

题目描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串: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

[BZOJ1014][JSOI2008]火星人prefix splay+hash+二分

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1014 先考虑如果没有修改操作和插入操作,是一个静态的字符串,我们可以怎样快速求得题目中的LCQ. 两个字符串判等很容易想到hash.于是我们二分答案并二分判断,就可以在$log_n$时间内得到答案. 现在加上修改和插入操作,其实就是要动态维护子串也就是一段区间的hash值,这种问题很容易就想到用splay来维护. 每个节点记录此节点管辖下子树的hash值h,当前节点的h=左孩子的h+节点

【BZOJ1014】【JSOI2008】火星人prefix Splay处理区间,hash+dichotomy(二分)check出解

题意不赘述了,太清晰了. 说题解:首先根据原字符串建立SPT,首尾建议多加一个空白字符. 给一个树构图,按照平衡树的前后大小顺序性质可以使它们始终维持为一个序列,并且可以通过rank找到序列的第k个. 树构造完了以后,点插入,点修改,询问神马的代码里都有详细注释. /* BZOJ 1014 新手看的时候建议从main函数处开始,按照运行顺序来脑模拟. P.S. 这个代码的hash用的是自然溢出而非取mod运算. */ #include <cstdio> #include <cstdlib

bzoj 1014 [JSOI2008]火星人prefix (splay+二分答案+字符串hash)

题目大意:维护一个字符串,支持插入字符和替换字符的操作,以及查询该字符串两个后缀的最长公共前缀长度 乍一看以为是后缀数组,然而并没有可持久化后缀数组(雾) 看题解才知道这是一道splay题,首先要对splay维护区间信息有一定了解 splay维护,插入字符,替换字符 而它的字树内所有儿子的中序遍历的hash值也可以通过splay维护  (这个推导式似乎烂大街了) 而后缀就是把i-1拎到根节点,然后把n+1拎到根节点的右儿子上,它的左儿子表示的就是hash值 至于如何查公共前缀呢?二分答案啊!询问

【bzoj1014】: [JSOI2008]火星人prefix 平衡树-字符串-hash-二分

[bzoj1014]: [JSOI2008]火星人 用平衡树维护字符串的hash 然后询问的时候二分一下就好了 1 /* http://www.cnblogs.com/karl07/ */ 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <algorithm> 7 using namespace std; 8 #

[BZOJ1014] [JSOI2008] 火星人prefix (splay &amp; 二分答案)

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,