[bzoj1014]:[JSOI2008]火星人prefix

传送门
这个题我一眼秒,然后发现好像是查询好像变成了 O(log2n)
然后发现数据范围说查询操作不超过10000次,然后就觉得应该是正解了。。
然后调试了超级久。。。
再然后就WA了3个点。。
最后发现我调试的时候为了方便,质数只取到了1000,然后取到1e9+7,就过了。。
我们来说正解吧。


emmmm,一句话题解吧。
Splay+Hash+二分
好像有点儿少,还是具体说说吧。。
首先,因为有插入操作,还有修改,还有查询,所以肯定要用Splay
具体用法的话可以参考NOI2005维修数列
然后还有字符串Hash,正常Hash就好了,出题人没有卡Hash。
(注意,这个题时间比较紧,不要取模,自然溢出就好了)
然后查询的时候,我们只要二分一个答案mid
然后判断[x,x+mid-1]和[y,y+mid-1]这两段的Hash值是否相同就好了
相同,则l=mid
不相同,则r=mid-1
然后取mid的时候要这样取mid=(l+r+1)>>1
然后还要while(l < r)
最后返回l
不要问我怎么知道这么做的
(其实是试出来的)
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll unsigned long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';int f=1;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
const int N=2e5+5;
const ll p=1e9+7;
char cc[N];
int n,m,sz,root;
int f[N],ch[N][2];
ll fac[N],val[N],sum[N],size[N];
inline void pushup(int x){
    int lc=ch[x][0],rc=ch[x][1];
    sum[x]=sum[lc]*(fac[size[rc]+1])+sum[rc]+val[x]*fac[size[rc]];
    size[x]=size[lc]+size[rc]+1;
}
inline bool get(int x){return ch[f[x]][1]==x;}
inline void rotate(int x){
    int y=f[x],z=f[y],w=get(x),w2=get(y);
    ch[y][w]=ch[x][w^1];f[ch[y][w]]=y;
    ch[x][w^1]=y;f[y]=x;
    f[x]=z;if(z)ch[z][w2]=x;
    pushup(y);pushup(x);
}
inline void splay(int x,int goal){
    // cout<<"splay "<<x<<" to "<<goal<<endl;
    for(int fa=0;(fa=f[x])!=goal;rotate(x))
        if(f[fa]!=goal)
            rotate((get(fa)==get(x))?fa:x);
    if(!goal)root=x;
    // cout<<"   success!"<<endl;
}
inline void build(int &x,int l,int r,int fa){
    if(l>r){x=0;return;}
    int mid=(l+r)>>1;
    x=++sz;val[sz]=sum[sz]=cc[mid-1]-'a'+1;size[sz]=1;f[sz]=fa;
    if(mid==1)val[sz]=sum[sz]=0;
    if(mid==n+2)val[sz]=sum[sz]=0;
    if(!(l^r))return;
    build(ch[x][0],l,mid-1,x);
    build(ch[x][1],mid+1,r,x);
    pushup(x);
}
inline int kth(int k){
    int now=root;
    while(1){
        if(ch[now][0]&&k<=size[ch[now][0]])now=ch[now][0];
        else{
            int tmp=size[ch[now][0]]+1;
            if(k<=tmp){return now;}
            k-=tmp;now=ch[now][1];
        }
    }
}
inline void print(int);
inline int split(int L,int R){
    if(L>R)return 0;
    L++;R++;
    // cout<<"split "<<L-1<<' '<<R-1<<endl;
    int l=kth(L-1);splay(l,0);
    int r=kth(R+1);splay(r,l);
    // cout<<l<<' '<<r<<endl;
    // cout<<"   success!"<<endl;
    // print(root);
    return ch[r][0];
}
inline ll query(int L,int R){
    int now=split(L,R);
    return sum[now];
}
inline void insert(int pos,char c){
    n++;
    int now=split(pos,pos);
    val[++sz]=sum[sz]=c-'a'+1;size[sz]=1;
    f[sz]=now;ch[now][1]=sz;
    pushup(now);pushup(ch[root][1]);pushup(root);
    // print(root);
}
inline void change(int pos,char c){
    int now=split(pos,pos);
    val[now]=sum[now]=c-'a'+1;size[now]=1;
    pushup(ch[root][1]);pushup(root);
}
inline int find(int x,int y){
    if(x>y)swap(x,y);
    ll pre1=query(x,x);
    ll pre2=query(y,y);
    if(pre1!=pre2)return 0;
    int l=1,r=n-y+1,mid;
    while(l<r){
        // cout<<"find "<<l<<' '<<r<<endl;
        mid=(l+r+1)>>1;
        ll v1=query(x,x+mid-1);
        // cout<<v1<<' ';
        ll v2=query(y,y+mid-1);
        // cout<<v2<<endl;
        if(v1==v2)l=mid;
        else r=mid-1;
        // system("pause");
    }
    return l;
}
inline void print(int now){
    cout<<endl;
    cout<<"id:  "<<now<<endl;
    cout<<"val: "<<val[now]<<endl;
    cout<<"sum: "<<sum[now]<<endl;
    cout<<"fa:  "<<f[now]<<endl;
    cout<<"lc:  "<<ch[now][0]<<endl;
    cout<<"rc:  "<<ch[now][1]<<endl;
    cout<<endl;
    // system("pause");
    if(ch[now][0])print(ch[now][0]);
    if(ch[now][1])print(ch[now][1]);
}
int main(){
    scanf("%s",cc+1);
    m=read();n=strlen(cc+1);fac[0]=1;
    for(int i=1;i<N;i++)fac[i]=fac[i-1]*p;
    build(root,1,n+2,0);sz=n+2;
    // print(root);
    while(m--){
        int x,y;
        char opt[3],c[3];
        scanf("%s",opt);
        if(opt[0]=='Q')x=read(),y=read(),printf("%d\n",find(x,y));
        if(opt[0]=='R')x=read(),scanf("%s",c),change(x,c[0]);
        if(opt[0]=='I')x=read(),scanf("%s",c),insert(x,c[0]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值