蚯蚓排队题解

蚯蚓排队

题目描述

蚯蚓幼儿园有\(n\)只蚯蚓。幼儿园园长神刀手为了管理方便,时常让这些蚯蚓们列队表演。

所有蚯蚓用从\(1\)\(n\)的连续正整数编号。每只蚯蚓的长度可以用一个正整数表示,根据入园要求,所有蚯蚓的长度都不超过\(6\)。神刀手希望这些蚯蚓排成若干个队伍,初始时,每只蚯蚓各自排成一个仅有一只蚯蚓的队伍,该蚯蚓既在队首,也在队尾。

神刀手将会依次进行\(m\)次操作,每个操作都是以下三种操作中的一种:

  1. 给出\(i\)\(j\),令\(i\)号蚯蚓与\(j\)号蚯蚓所在的两个队伍合并为一个队伍,具体来说,令\(j\)号蚯蚓紧挨在\(i\)号蚯蚓之后,其余蚯蚓保持队伍的前后关系不变。

  2. 给出\(i\),令\(i\)号蚯蚓与紧挨其后的一只蚯蚓分离为两个队伍,具体来说,在分离之后,\(i\)号蚯蚓在其中一个队伍的队尾,原本紧挨其后的那一只蚯蚓在另一个队伍的队首,其余蚯蚓保持队伍的前后关系不变。

  3. 给出一个正整数\(k\)和一个长度至少为\(k\)的数字串\(s\),对于\(s\)的每个长度为\(k\)的连续子串\(t\) (这样的子串共有$\left| s\right|-k+1 $ 个,其中\(\left| s\right|\)\(s\)的长度),定义函数\(f(t)\) ,询问所有这些的乘积对\(998244353\) 取模后的结果。其中 \(f(t)\) 的定义如下:

对于当前的蚯蚓队伍,定义某个蚯蚓的向后\(k\)数字串为:从该蚯蚓出发,沿队伍的向后方向,寻找最近的\(k\)只蚯蚓(包括其自身),将这些蚯蚓的长度视作字符连接而成的数字串;如果这样找到的蚯蚓不足\(k\)只,则其没有向后数字串。而 \(f(t)\) 表示所有蚯蚓中,向后\(k\)数字串恰好为\(t\)的蚯蚓只数。

输入格式

输入文件的第一行有两个正整数\(n\)\(m\),分别表示蚯蚓的只数与操作次数。

第二行包含\(n\)个不超过\(6\)的正整数,依次表示编号为$ 1,2,3,\dots,n $的蚯蚓的长度。

接下来\(m\)行,每行表示一个操作。每个操作的格式可以为:

  • $ 1\ i\ j $( $ 1\le i,j\le n $ )表示:令\(i\)号与\(j\)号蚯蚓所在的两个队伍合并为一个队伍,新队伍中,\(j\)号蚯蚓紧挨在\(i\)号蚯蚓之后。保证在此操作之前,\(i\)号蚯蚓在某个队伍的队尾,\(j\)号蚯蚓在某个队伍的队首,且两只蚯蚓不在同一个队伍中。

  • $ 2\ i $($1<i<n $ )表示:令\(i\)号蚯蚓与紧挨其后一个蚯蚓分离为两个队伍。保证在此操作之前,\(i\)号蚯蚓不是某个队伍的队尾。

  • $ 3\ s\ k $ (\(k\)为正整数,\(s\)为一个长度至少为\(k\)的数字串)表示:询问\(s\)的每个长度为\(k\)的子串\(t\)\(f(t)\) 的乘积,对\(998244353\)取模的结果。\(f(t)\) 的定义见题目描述。

同一行输入的相邻两个元素之间,用恰好一个空格隔开。

输入文件可能较大,请不要使用过于缓慢的读入方式。

输出格式

输出到标准输出。

依次对于每个形如 \(3\ s\ k\) 的操作,输出一行,仅包含一个整数,表示询问的结果。

数据范围与提示

保证 $ n\le 2\times 10^5 $, $ m\le 5\times 10^5 $ ,\(k\le 50\)

设 $ \sum \left| s\right| $ 为某个输入文件中所有询问的\(s\) 的长度总和,则 $ \sum \left| s\right| \le 10^7 $。

\(c\) 为某个输入文件中形如$ 2\ i$ 的操作的次数,则 $ c\le 10^3 $ 。

题解

自我感觉这题够恶心的,题面很长还难理解,大概是说给出\(n\)个长度为\(1\)的字符串,支持三种操作:合并两个字符串,拆分一个字符串,给定一个长度至少为\(k\)的字符串\(s\),将其所有长为\(k\)的子串取出,对于每个子串,\(f(t)\)为在蚯蚓组成的字符串中相等的个数。求 $ \prod f(t) $ 。

一眼就不很会,wzh先生直接投降看题解了,要用哈希表,然后我也去学哈希表,再来做题。

不得不说,这数据出的太绝了。直接想法:

对于操作\(3\),当然是枚举s的子串计算\(hash\),再利用哈希表计算\(f(t)\),总复杂度 \(O(\left| s\right|)\),可行,但需要预知现有的每个蚯蚓字符串的状态(hash)。

对于操作\(1,2\),我们很明显需要统计新出现的字符串的\(hash\),放入哈希表,以便操作\(3\),但这样单次复杂度貌似是\(O(n^2)\)

然后就不知道怎么办了。

感谢wzh先生的提示

你看这个K它多可爱啊

你看这个c它多可爱啊

思考,思考......悟了!


当合并两个字符串时,由于保证 \(K\le 50\),所以长度超过50的子串是无用的,因此每次合并,只需预处理长度不超过\(50\)的字符串的哈希值,利用链表保存每只蚯蚓的前驱和后继,每次操作 \(O(K^2)\)

这样看来,最坏复杂的 \(O(mK^2)\),好像还是不能过。

\(c\le 1000\) ,复杂度 \(O(cK^2)\),可以忽略不计。这样字符串合并后可以看作不会再拆开,子串的长度只有 \(nK\)个,复杂度\(O(nK)\)

总复杂度 \(O(nk+ck^2+\left| s\right|)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int N=1e6;
const int mod=998244353;
const int M=1e7+19;
const int base=233;
const int K=50;
int n,m,a,b,k,l[N],t;
int h[N],p[N];
int cnt,head[N*50],nxt[N*50],num[N*50],sum[N*50],to[N],fo[N];
char s[N*10+14];
void insert(int key)
{
    int hash=key%M;
    for(int i=head[hash];i;i=nxt[i]){
        if(num[i]==key){
            sum[i]++;
            return ;
        }
    }
    cnt++;
    sum[cnt]=1;
    nxt[cnt]=head[hash];
    head[hash]=cnt;
    num[cnt]=key;
}
void cutoff(int key)
{
    int hash=key%M;
    for(int i=head[hash];i;i=nxt[i]){
        if(num[i]==key){
            sum[i]--;
            return ;
        }
    }
}
int query(int key)
{
    int hash=key%M;
    for(int i=head[hash];i;i=nxt[i]){
        if(num[i]==key){
            return sum[i];
        }
    }
    return 0;
}
inline int read()
{
    int f=1,w=0;
    char c=getchar();
    while(c>'9'||c<'0'){
        c=getchar();
    }
    while(c<='9'&&c>='0'){
        w=w*10+(c-'0');
        c=getchar();
    }
    return w*f;
}
void write1(int x){
    if(x>9) write1(x/10);
    putchar(x%10+48);
}
inline void write(int x){
    write1(x);
}
signed main()
{
    n=read();
    m=read();
    p[0]=1;
    for(int i=1;i<=K;i++){
        p[i]=(p[i-1]*base);
    }
    for(int i=1;i<=n;i++){
        l[i]=read();
        fo[i]=i;
        to[i]=i;
        insert((l[i]+'0'));
    }
    while(m--){
        t=read();
        if(t==1){
            a=read();b=read();
            int x=a,w=b,z,y;
            int hx=0,hy=0;
            for(int i=1;i<K;i++){
                if(fo[x]==x)break;
                x=fo[x];
            }
            for(int i=1;i<K;i++){
                if(to[w]==w)break;
                w=to[w];
            }
            z=a;
            for(int i=1;;i++){
                y=b;
                hx=(l[z]+'0')*p[i-1]+hx;
                hy=0;
                for(int j=1;;j++){
                    hy=hy*base+(l[y]+'0');
                    insert(hx*p[j]+hy);
                    if(y==w||j+i>=K)break;
                    y=to[y];
                }
                if(z==x)break;
                z=fo[z];
            }
            fo[b]=a;
            to[a]=b;
        }
        else if(t==2){
            a=read();
            b=to[a];
            int x=a,w=b,z,y;
            int hx=0,hy=0;
            for(int i=1;i<K;i++){
                if(fo[x]==x)break;
                x=fo[x];
            }
            for(int i=1;i<K;i++){
                if(to[w]==w)break;
                w=to[w];
            }
            z=a;
            for(int i=1;;i++){
                y=b;
                hx=hx+(l[z]+'0')*p[i-1];
                hy=0;
                for(int j=1;;j++){
                    hy=hy*base+(l[y]+'0');
                    cutoff((hx*p[j])+hy);
                    if(y==w||j+i>=K)break;
                    y=to[y];
                }
                if(z==x)break;
                z=fo[z];
            }
            fo[b]=b;
            to[a]=a;
        }
        else{
            scanf("%s",s);
            k=read();
            int shadow=0,ans=1,i;
            bool flag=0;
            for(i=0;i+1<k;i++){
                shadow=(shadow*base+s[i]);
            }
            int len=strlen(s);
            for(i=0;i+k-1<len;i++){
                flag=1;
                if(i==0){
                    shadow=(shadow*base+s[i+k-1]);
                    ans*=query(shadow);
                    ans%=mod;
                }
                else{
                    shadow=shadow*base-s[i-1]*p[k]+s[i+k-1];
                    ans*=query(shadow);
                    ans%=mod;
                }
            }
            if(!flag)ans=0;
            write(ans);puts(" ");
        }
    }
}

后续

题还是比较难想的,调了一下午后还是\(TLE\ 88pts\),最后不得不先放下了,集训最后一天又拿出来,\(hangry\)\(CuFeO4\)帮我卡常,但越卡越T,甚至WA了,最后想起来哈希表取模用的\(M=1e6+19\),太小了,这样哈希冲突的概率更大,链表的长度更大,所以查询,修改速度慢了,暴力改为\(1e7+19\),AC

posted @ 2024-02-06 15:43  Abnormal123  阅读(47)  评论(4)    收藏  举报