蚯蚓排队题解

蚯蚓排队

题目描述

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

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

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

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

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

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

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

输入格式

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

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

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

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

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

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

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

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

输出格式

输出到标准输出。

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

数据范围与提示

保证 n2×105m5×105k50

|s| 为某个输入文件中所有询问的s 的长度总和,则 |s|107

c 为某个输入文件中形如2 i 的操作的次数,则 c103

题解

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

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

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

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

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

然后就不知道怎么办了。

感谢wzh先生的提示

你看这个K它多可爱啊

你看这个c它多可爱啊

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


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

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

c1000 ,复杂度 O(cK2),可以忽略不计。这样字符串合并后可以看作不会再拆开,子串的长度只有 nK个,复杂度O(nK)

总复杂度 O(nk+ck2+|s|)

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,最后不得不先放下了,集训最后一天又拿出来,hangryCuFeO4帮我卡常,但越卡越T,甚至WA了,最后想起来哈希表取模用的M=1e6+19,太小了,这样哈希冲突的概率更大,链表的长度更大,所以查询,修改速度慢了,暴力改为1e7+19,AC

posted @   Abnormal123  阅读(36)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示