首页 > 其他分享 >蚯蚓排队题解

蚯蚓排队题解

时间:2024-02-06 15:56:21浏览次数:27  
标签:le hash int 题解 排队 队伍 长度 蚯蚓

蚯蚓排队

题目描述

蚯蚓幼儿园有\(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

标签:le,hash,int,题解,排队,队伍,长度,蚯蚓
From: https://www.cnblogs.com/abnormal123/p/18009842

相关文章

  • Codeforces Round 921 (Div. 1) 题解
    HelloAd-HocForces!A字符集为前\(k\)个小写字母,给定长度为\(m\)的字符串,求所有的长度为\(n\)的字符串是否是这个字符串的子串。(此处字串不连续)如果不是需要给出反例。\(1\len,k\le26\),\(1\lem\le1000\)。\(\sumn,\summ\le10^6\)sol.D1A就是神秘贪心,汗流浃背......
  • U405333 帕鲁世界迷路的一天 题解
    题目链接:帕鲁世界迷路的一天前置弱化版:P3604美好的每一天题解一个非常简单的普通莫队解很容易写出来,具体的看我前置弱化版题解,然而这个复杂度高达\(O(26n\sqrt{q})\),显然无法通过强化版。一种看上去很正确的“假解”我们思考如何去掉这个\(26\),我们猜想:能够组成\(pre[c......
  • [ARC171] A~D 题解
    [ARC171]A~D题解A.NoAttacking最优策略是车隔行放,分讨一下就可以了。if(n<a)cout<<"No\n";else{if(a*2<n)b-=(a+1)*(n-a);else{b-=(n-a)*(n-a);if(b<=0)cout<<"Yes\n";......
  • P3219 [HNOI2012] 三角形覆盖问题&P1222 三角形 题解
    严格单$\log$做法,附实现细节和代码。考虑从左往右扫描线,发现每次操作是线段上端点$-1$,插入线段,删除退化成点的线段。如果某时刻一条线段被另一条完全覆盖,那么这条线段显然不会产生贡献,可以删去。最后得到的线段一定是左端点单调递增时,右端点也单调递增,可以用set维护。当两......
  • P10145 [WC/CTS2024] 线段树 题解
    Link纪念一下场切题。题意:给定一棵(分点不一定为中点)的线段树,给定若干个询问区间,问有多少个线段树上结点的集合,知道了这些结点对应的区间和就可以知道任何一个询问区间的和。从询问区间开始考虑。会发现可以把所有\(a_i\)分成若干个集合,只要知道每个集合的和就可以知道所有询......
  • 浅谈甲类生产厂房应急照明和疏散指示系统的设计问题解析
    摘要:文章结合电气设计项目实践经验,分析了甲类生产厂房消防应急照明和疏散指示系统设计中照明灯和标志灯的设置、疏散走道和疏散通道的规划、集中控制型系统类型的选择、电压等级的选择中存在的问题,总结了相关经验,可以为同类工程提供参考。关键词:甲类生产厂房;消防应急照明和疏散指示......
  • 题解 CF1876B
    题意给定一个数组\([a_1,a_2,a_3.\cdots,a_n]\),一开始所有元素都为白色。你可以选定其中的至少一个元素涂成黑色,共有\(2^n-1\)种涂法。此时对于所有黑色元素\(a_i\),下标为\(i\)的倍数的所有白色元素都将变成绿色。此时数组中所有黑色和绿色元素的最大值记为此种涂法的......
  • 洛谷 P10145 [WC/CTS2024] 线段树 题解--zhengjun
    提供一种考场做法,在思路上和官方题解的差异蛮大的,虽然结果差不多。首先需要发现\([l,r)\)区间可以算出来的充要条件是:如果对于每个选中的节点\(u\),连无向边\((L_u,R_u)\),则当且仅当\(l\)和\(r\)连通时区间\([l,r)\)可以算出来。证明的话,用前缀和理解这些东西,分别考虑......
  • 题解 CF1849D
    萌新的第一篇题解题目大意对于一个初始颜色都为蓝色的数组\(a_1,a_2,\dots,a_n\)其中\(a_n\in\{0,1,2\}\),现在可以进行两个操作:花费\(1\)个金币,将\(a_i\)涂成红色;选择一个红色的\(a_i>0\),将\(a_{i-1}\)或\(a_{i+1}\)涂成红色,同时\(a_i\)减\(1\)。输出金......
  • F. 乘积最大(题解)
    题目今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:设有一个长度为......