洛谷P3823/LOJ2303/UOJ315/BZOJ4943[NOI2017]蚯蚓排队(字符串哈希+哈希表)

看见神刀手我好像想到了什么。。。

字符串匹配问题。很明显要用字符串哈希,而且发现题目中的$k$很小,考虑把长度小于等于$k$的字串哈希值都暴力存进哈希表哈希套哈希,那么匹配的时候就可以做到接近$O(1)$。

连接和删除时,新增或减少的子串一定跨越断点,也就是一定由前串的后缀和后串的前缀拼成,枚举即可。

复杂度$O(mk^2+\sum{|s|)}$,还是可以过的。

#include<cstdio>
#include<cctype>
typedef unsigned long long ull;
const int P=19260817;
const int mod=998244353;
const int N=200050;
char rB[1<<21],*rS,*rT,wB[1<<21];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline void pc(char c){if(wp+1==(1<<21))flush();wB[++wp]=c;}
inline int rd(){
    char c=gc();
    while(!isdigit(c))c=gc();
    int x=c&15;
    for(c=gc();isdigit(c);c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[15];
inline void wt(int x){
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    pc(x|48);
    while(l>=0)pc(buf[l--]|48);
    pc('\n');
}
int a[N],lst[N],nxt[N];
ull f[N];
char s[10000050];
struct Hash{
    int h[P],l[N<<7],cnt[N<<7],nxt[N<<7],sz;
    ull v[N<<7];
    inline void ins(ull x,int len){
        int pos=x%P,k=h[pos];
        for(;k;k=nxt[k])if(l[k]==len&&v[k]==x){++cnt[k];return;}
        v[++sz]=x;l[sz]=len;cnt[sz]=1;nxt[sz]=h[pos];h[pos]=sz;
    }
    inline void del(ull x,int len){
        for(int k=h[x%P];k;k=nxt[k])if(l[k]==len&&v[k]==x){--cnt[k];return;}
    }
    inline int query(ull x,int len){
        for(int k=h[x%P];k;k=nxt[k])if(l[k]==len&&v[k]==x)return cnt[k];
        return 0;
    }
}h;
inline int rds(){
    char c=gc();
    int len=1;
    while(!isdigit(c))c=gc();
    for(s[0]=c,c=gc();isdigit(c);c=gc())s[len++]=c;
    return len;
}
int main(){
    int n=rd(),q=rd(),i,j,opt,x,y,l1,l2,ans;
    ull res,tmp;
    for(f[0]=1ull,i=1;i<n;++i)f[i]=f[i-1]*7ull;
    for(i=1;i<=n;++i)h.ins(a[i]=rd(),1);
    while(q--){
        opt=rd();
        if(opt==1){
            x=rd();y=rd();
            nxt[lst[y]=x]=y;
            for(i=x,l1=1,res=0ull;i&&l1<50;i=lst[i],++l1){
                tmp=(res+=a[i]*f[l1-1]);
                for(j=y,l2=1;j&&l1+l2<=50;j=nxt[j],++l2)h.ins(tmp=tmp*7ull+a[j],l1+l2);
            }
        }else if(opt==2){
            lst[y=nxt[x=rd()]]=0;nxt[x]=0;
            for(i=x,l1=1,res=0ull;i&&l1<50;i=lst[i],++l1){
                tmp=(res+=a[i]*f[l1-1]);
                for(j=y,l2=1;j&&l1+l2<=50;j=nxt[j],++l2)h.del(tmp=tmp*7ull+a[j],l1+l2);
            }
        }else if(opt==3){
            l1=rds();x=rd();
            for(res=i=0;i<x;++i)res=res*7ull+(s[i]&15);
            ans=h.query(res,x);
            for(i=x;i<l1&&ans;++i)ans=(ull)ans*h.query(res=(res-(s[i-x]&15)*f[x-1])*7ull+(s[i]&15),x)%mod;
            wt(ans);
        }
    }
    flush();
    return 0;
}
View Code

然后你会发现在洛谷上TLE了,这时开一个O2就过了不要慌

然后你发现在bzoj土豆机上玄学MLE了,这时候放心大胆地把Hash里面的N<<7继续开大到50000050就过了

或许是数组越界?

posted @ 2019-08-08 16:40  wangyuchen  阅读(178)  评论(0编辑  收藏  举报