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

来源:互联网 发布:淘宝评论被屏蔽 编辑:程序博客网 时间:2024/05/16 15:54

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 5420  Solved: 1713
[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个数据,没有插入操作。

Source


    面对这题我整个人是崩溃的…… splay党,无限TLE。

    于是交了一发标程,卡着时限过了。

    不过我现在知道怎么略微优化让它跑过了,但是…… 我懒得改了 = =

    先贴一发代码先:splay维护字符串,维护区间的hash值。 代码下面再说怎么优化 - -

#include "stdio.h"#include "iostream"#define rep(f,a,b) for(f=a;f<=b;f++)#define Empty null,null,nullusing namespace std;typedef long long ll;const int L=100005,M=1<<7;const ll mod=9875321,di=(ll)1e4;struct node {node*c[2],*fa; char val; int siz; ll hash;void ins(ll h,node*c0,node*c1,node*f,char c);void pushup(); node();} Tnull,*null=&Tnull,*root;ll mul[L]; char s[L]; int len,q;node:: node(){ c[0]=c[1]=fa=null; siz=hash=0; val='\0';}inline ll mu(ll a,ll b,ll m){ll c=a%di*b,d=a/di*b;c%=m; d%=m; d*=di;return (c+d)%m;}void node:: pushup(){siz=c[0]->siz+c[1]->siz+1;hash=c[1]->hash+val*mul[c[1]->siz]+mu(c[0]->hash,mul[c[1]->siz+1],mod);hash%=mod;}void node:: ins(ll h,node* c0,node* c1,node* f,char ch){hash=h; val=ch; c[0]=c0,c[1]=c1,fa=f; siz=1;}void read(int&a){ char ch=getchar(); a=0;while (ch<'0'||ch>'9') ch=getchar();while (ch<='9'&&ch>='0') a=a*10+ch-'0',ch=getchar();}int readin(char *s){ char ch=getchar(); int cnt=0;while (ch==' '||ch=='\n') ch=getchar();do s[++cnt]=ch,ch=getchar(); while (ch!=' '&&ch!='\n');return cnt;}void rotate(node*x,node*&k){node *y=x->fa,*z=y->fa; if(y==k&&y==root) k=x;int l=y->c[1]==x,r=l^1; x->fa=z,y->fa=x;    if(x->c[r]!=null) x->c[r]->fa=y;if (z!=null) if(z->c[0]==y) z->c[0]=x; else z->c[1]=x;y->c[l]=x->c[r]; x->c[r]=y; y->pushup(); x->pushup();}void splay(node*x,node*&k){while(x!=k){node*y=x->fa,*z=y->fa;if(y!=k){if (y->c[0]==x^z->c[0]==y) rotate(x,k);else rotate(y,k);}rotate(x,k);}if (x->fa!=null) x->fa->pushup();}node*build(char*c,int len){int mid=(len+1)>>1; node*r=new node;if(len<=0) return null;if(len==1) return (r->ins(c[1],Empty,c[1]),r);r->ins(0,build(c,mid-1),build(c+mid,len-mid),null,c[mid]);r->pushup(); if(r->c[0]!=null) r->c[0]->fa=r;r->c[1]->fa=r; return r;}node*find(int rank,node*now){if (rank==now->c[0]->siz) return now;if (rank<now->c[0]->siz) return find(rank,now->c[0]);return find(rank-now->c[0]->siz-1,now->c[1]);}void init(){ len=readin(s+1); int i; mul[0]=1;rep(i,1,L-2) mul[i]=mul[i-1]*M%mod;root=build(s,len+2);}void split(int x,node*&to){ node*t=find(x,root); splay(t,to);}void remove(int x,char c){ split(x,root); root->val=c; root->pushup();}void insert(int x,char c){ split(x+1,root); split(x,root->c[0]); node*t=new node;t->ins(0,null,null,root->c[0],c); root->c[0]->c[1]=t;root->c[0]->c[1]->pushup(); root->c[0]->pushup(); root->pushup(); }void query(int x,int y){ if(x>y) swap(x,y);int bot=0,mid,top=root->siz-y-1;do { mid=(bot+top+1)>>1;split(x-1,root); split(x+mid,root->c[1]);ll a=root->c[1]->c[0]->hash;split(y-1,root); split(y+mid,root->c[1]);ll b=root->c[1]->c[0]->hash;if (a==b) bot=mid; else top=mid-1;} while (bot<top); int ans=bot;printf("%d\n",ans);}void work(){ char c,ch; int x,y,i; read(q);rep(i,1,q) {readin((&c)-1);if(c=='Q'){read(x),read(y);query(x,y);} else {read(x),readin((&ch)-1);if(c=='I') insert(x,ch);else remove(x,ch);}}}int main(){//freopen("prefix.in","r",stdin);//freopen("prefix.out","w",stdout);init(); work();return 0;}

    对TLE,我只想说一句话:毒瘤! - -

    好吧现在考虑怎么优化。总结了一下我能想到的有以下几点:

      1.将long long的大素数改为int的素数(如9875321),虽然冲突概率增加,但是能加快速度。如果真的怕冲突,可以用2个short的素数做双hash

      2.在适当的函数前加inline

      3.同步建一棵splay,旋转分别在两棵树上进行,询问的常数大致能降一半左右

      4.对于二分答案的区间小到一定值,如100时,暴力获取之后的数据进行暴力对比

    然而,我实在是懒得去实现了qwq。如果有谁用以上方法将我的程序改进后A了,劳烦评论下,感激不尽- -

0 0
原创粉丝点击