bzoj4943 [Noi2017]蚯蚓(Hash+复杂度分析卡常)

做法较为明显,直接模拟即可。Hash比较字符串。
复杂度分析一波,如果没有2操作的话,复杂度显然是 O ( n k ) 的,考虑有2操作的话,分裂两个蚯蚓的复杂度是 O ( k 2 ) ,因此而带来的合并复杂度也是 O ( c k 2 ) 的,因此总的复杂度就是 O ( n k + c k 2 + | s | )
然后就是卡常啦!
首先我们离线操作,先把可能会询问到的串的Hash值扔进Hash表,不会被问到的Hash值就不存了,这样可以保证Hash的节点最多 O ( | s | )

然后就是Hash表玄学技巧啦!因为最多1e7个点,所以分到1e8个桶内比较科学。

不过这样uoj过不去!亲测可过的Hash表模数为10233333~

#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define inf 0x3f3f3f3f
#define N 200010
#define mod 998244353
#define k1 11113
#define Mod 10233333
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,nn,a[N],h[Mod],num=0,pre[N],succ[N],now=1;
ull bin[60],b[N][60];
int s[10000010];
inline void get_S(){
    char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch>='0'&&ch<='9') s[now++]=ch-'0',ch=gc();
}
struct Hash_table{
    ull key;int val,next;
}data[10000010];
struct quer{
    int op,x,y,k;
}qq[500010];
inline void ins1(ull key){
    int x=key%Mod;
    for(int i=h[x];i;i=data[i].next)
        if(data[i].key==key) return;
    data[++num].key=key;data[num].val=0;data[num].next=h[x];h[x]=num;
}
inline void ins(ull key,int val){
    int x=key%Mod;
    for(int i=h[x];i;i=data[i].next)
        if(data[i].key==key){data[i].val+=val;return;}
}
inline int hs(ull key){
    int x=key%Mod;
    for(int i=h[x];i;i=data[i].next)
        if(data[i].key==key) return data[i].val;return 0;
}
int main(){
//  freopen("queue25.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();m=read();bin[0]=1;
    for(int i=1;i<=50;++i) bin[i]=bin[i-1]*k1;
    for(int i=1;i<=n;++i) a[i]=read();
    for(int ii=1;ii<=m;++ii){
        qq[ii].op=read();
        if(qq[ii].op==3){
            qq[ii].x=now;get_S();qq[ii].y=now-1;int k=qq[ii].k=read();ull tmp=0;
            for(int i=1;i<=k;++i) tmp=tmp*k1+s[qq[ii].x+i-1];ins1(tmp);
            for(int i=qq[ii].x+k;i<=qq[ii].y;++i){
                tmp-=s[i-k]*bin[k-1];tmp=tmp*k1+s[i];ins1(tmp);
            }continue;
        }qq[ii].x=read();if(qq[ii].op==1) qq[ii].y=read();
    }for(int i=1;i<=n;++i) b[i][1]=a[i],ins(b[i][1],1);
    for(int ii=1;ii<=m;++ii){
        int op=qq[ii].op;
        if(op==3){
            int k=qq[ii].k;ull tmp=0;
            for(int i=1;i<=k;++i) tmp=tmp*k1+s[qq[ii].x+i-1];ll res=hs(tmp);
            for(int i=qq[ii].x+k;i<=qq[ii].y;++i){
                tmp-=s[i-k]*bin[k-1];tmp=tmp*k1+s[i];
                res=res*hs(tmp)%mod;
            }printf("%lld\n",res);continue;
        }int x=qq[ii].x;
        if(op==1){
            int y=qq[ii].y;pre[y]=x;succ[x]=y;
            for(int i=1;i<50;++i){
                if(!x) break;
                for(int j=i+1;j<=50;++j){
                    if(!b[y][j-i]) break;
                    b[x][j]=b[x][i]*bin[j-i]+b[y][j-i];ins(b[x][j],1);
                }x=pre[x];
            }
        }else{
            int y=succ[x];pre[y]=0;succ[x]=0;
            for(int i=1;i<50;++i){
                if(!x) break;
                for(int j=i+1;j<=50;++j){
                    if(!b[y][j-i]) break;ins(b[x][j],-1);b[x][j]=0;
                }x=pre[x];
            }
        }
    }return 0;
}

猜你喜欢

转载自blog.csdn.net/icefox_zhx/article/details/80844014