zoukankan      html  css  js  c++  java
  • [NOI2017]蚯蚓排队(链表+hash)

    这题看题面感觉挺玄学的,但其实会挂链式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
    ",query());
        }
    }
    View Code
  • 相关阅读:
    ubuntu安装jdk的两种方法
    LeetCode 606. Construct String from Binary Tree (建立一个二叉树的string)
    LeetCode 617. Merge Two Binary Tree (合并两个二叉树)
    LeetCode 476. Number Complement (数的补数)
    LeetCode 575. Distribute Candies (发糖果)
    LeetCode 461. Hamming Distance (汉明距离)
    LeetCode 405. Convert a Number to Hexadecimal (把一个数转化为16进制)
    LeetCode 594. Longest Harmonious Subsequence (最长的协调子序列)
    LeetCode 371. Sum of Two Integers (两数之和)
    LeetCode 342. Power of Four (4的次方)
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/11050317.html
Copyright © 2011-2022 走看看