[NOI2017]蚯蚓排队(链表+hash)

这题看题面感觉挺玄学的,但其实会挂链式hash就能暴力切了。

首先模数设成自然溢出ull,然后挂链时的模数取2^24。然后就可以直接hash了。对于3操作直接O(Σ|S|)询问即可,对于1、2操作,直接暴力加、减长度不超过50的字符,毕竟k<=50,这是个关键性条件。所以暴力能切了。

下面分析时间复杂度:先假设没有2操作,因为每一个位置只有长度不超过50的,每次加的数量也是不超过50的,这样总复杂度是均摊O(nk)的;再考虑2操作,因为2操作数量很少,而且每次只会至多影响O(k2)个数,因此考虑2操作后,复杂度是O(ck2)的,于是总复杂度是O(nk+ck2+Σ|S|)。注意hash不能用map,要挂链,因为map自带大常数和log,为什么不能unordered_map呢?因为这是NOI,不能用c++11。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=5e5+7,p=19260817,mod=998244353,gloid=(1<<24)-1;
int n,m,q,a[N],pre[N],nxt[N],cnt[N],f[150];
ull h[150],pw[150];
char s[11000000];
struct Hash{
    struct edge{ull x;int v,nxt;}e[21000000];
    int hd[gloid+1],ecnt;
    void add(ull x,int d)
    {
        int u=x&gloid;
        for(int i=hd[u];i;i=e[i].nxt)if(e[i].x==x){e[i].v+=d;return;}
        e[++ecnt]=(edge){x,d,hd[u]},hd[u]=ecnt;
    }
    int query(ull x)
    {
        int u=x&gloid;
        for(int i=hd[u];i;i=e[i].nxt)if(e[i].x==x)return e[i].v;
        return 0;
    }
}mp;
void merge()
{
    int x,y,L=51,R=50;scanf("%d%d",&x,&y);
    memset(f,0,sizeof f);
    for(int i=x;i&&L>1;i=pre[i])f[--L]=a[i];
    for(int i=y;i&&R<100;i=nxt[i])f[++R]=a[i];
    for(int i=1;i<=R;i++)h[i]=h[i-1]*p+f[i];
    for(int i=L;i<=50;i++)
    for(int j=51;j<=min(R,i+49);j++)
    mp.add(h[j]-h[i-1]*pw[j-i+1],1);
    nxt[x]=y,pre[y]=x;
}
void split()
{
    int x,y,L=51,R=50;scanf("%d",&x),y=nxt[x];
    memset(f,0,sizeof f);
    for(int i=x;i&&L>1;i=pre[i])f[--L]=a[i];
    for(int i=y;i&&R<100;i=nxt[i])f[++R]=a[i];
    for(int i=1;i<=R;i++)h[i]=h[i-1]*p+f[i];
    for(int i=L;i<=50;i++)
    for(int j=51;j<=min(R,i+49);j++)
    mp.add(h[j]-h[i-1]*pw[j-i+1],-1);
    nxt[x]=pre[y]=0;
}
int query()
{
    scanf("%s",s+1);
    int len=strlen(s+1),ret=1,k;
    scanf("%d",&k);
    ull val=0;
    if(k==1)
    {
        for(int i=1;i<=len;i++)ret=1ll*ret*cnt[s[i]]%mod;
        return ret;
    }
    for(int i=1;i<=len;i++)
    {
        val=val*p+s[i];
        if(i>k)val-=pw[k]*s[i-k];
        if(i>=k)ret=1ll*ret*mp.query(val)%mod;
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&q);
    pw[0]=1;for(int i=1;i<=50;i++)pw[i]=pw[i-1]*p;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]+='0',cnt[a[i]]++;
    while(q--)
    {
        int op;scanf("%d",&op);
        if(op==1)merge();
        else if(op==2)split();
        else printf("%d\n",query());
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/hfctf0210/p/11050317.html