[ NOI2017 ] [ Hash 复杂度分析 ] BZOJ4943

将所有字符串哈希后存入哈希表,合并或分裂时暴力更新。
这样复杂度看上去是 O(nk2) O ( n k 2 ) 的,实际上是 O(ck2+nk) O ( c k 2 + n k )
证明:
显然要使复杂度更大,合并的 2 2 个队列的长度一定是 k
假如使合并的队列长度为 S S ,这样的队列只有 nS 个,复杂度为 nSS2=nS n S S 2 = n S ,当 S=k S = k 时取最大值。
一次分裂最多会使复杂度增加 2k2 2 k 2 ,所以分裂的复杂度为 O(ck2) O ( c k 2 )
总复杂度为 O(ck2+nk) O ( c k 2 + n k )

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int SIZE=1<<25;
inline char nc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
char ss[30];
int Len;
inline void Print(int x){
    if(!x){
        putchar('0');
        putchar('\n');
        return;
    }
    for(Len=0;x;x/=10)ss[++Len]=x%10;
    while(Len)putchar(ss[Len--]+48);
    putchar('\n');
}
typedef unsigned long long ll;
const int D=131072-1;
const int T=10000000;
const int N=500010;
const int M=998244353;
const ll P=1000000007;
int g[N],len;
int c[N];
int k,n,m,x,y;
int nx[N],ls[N];
ll p[51];
struct Hash_Table{
    int s[D<<2],nx[D<<2],h[D+1],cnt;
    ll a[D<<2];
    inline void Add(ll x){
        int y=x&D;
        for(int i=h[y];i;i=nx[i])
            if(a[i]==x){
                s[i]++;
                return;
            }
        a[++cnt]=x;nx[cnt]=h[y];h[y]=cnt;s[cnt]=1;
    }
    inline void Erase(ll x){
        for(int i=h[x&D];i;i=nx[i])
            if(a[i]==x){
                s[i]--;
                return;
            }
    }
    inline int Query(ll x){
        for(int i=h[x&D];i;i=nx[i])
            if(a[i]==x)return s[i];
        return 0;
    }
}Hash[51];
inline void Get(){
    len=0;
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(;c>='0'&&c<='9';c=nc())g[++len]=c-'0';
}
inline void Update1(int x,int y){
    ll S=0,s;
    for(int j=1,Y=y;Y&&j<50;j++,Y=nx[Y]){
        S=S*P+c[Y];s=S;
        for(int i=1,X=x;X&&i+j<=50;i++,X=ls[X])
            s+=p[i+j-1]*c[X],Hash[i+j].Add(s);
    }
    nx[x]=y;ls[y]=x;
}
inline void Update2(int x,int y){
    ll S=0,s;
    for(int j=1,Y=y;Y&&j<50;j++,Y=nx[Y]){
        S=S*P+c[Y];s=S;
        for(int i=1,X=x;X&&i+j<=50;i++,X=ls[X])
            s+=p[i+j-1]*c[X],Hash[i+j].Erase(s);
    }
    ls[y]=nx[x]=0;
}
int main(){
    Read(n);Read(m);
    for(int i=1;i<=n;i++)Read(c[i]),Hash[1].Add(c[i]);
    p[0]=1;
    for(int i=1;i<=50;i++)p[i]=p[i-1]*P;
    for(int i=1;i<=m;i++){
        Read(k);
        if(k==1)Read(x),Read(y),Update1(x,y);else
        if(k==2)Read(x),Update2(x,nx[x]);else{
            Get();Read(k);
            ll s=0,t=p[k];int Res=1;
            for(int j=1;j<k;j++)s=s*P+g[j];
            for(int j=k;j<=len;j++){
                s=s*P+g[j]-t*g[j-k];
                Res=1ll*Res*Hash[k].Query(s)%M;
            }
            Print(Res);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值