NOI2017 蚯蚓排队

描述

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

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

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

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

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

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

对于当前的蚯蚓队伍,定义某个蚯蚓的向后 k 数字串为:从该蚯蚓出发,沿队伍的向后方向,寻找最近的 k 只蚯蚓(包括其自身),将这些蚯蚓的长度视作字符连接而成的数字串;如果这样找到的蚯蚓不足 k 只,则其没有向后k数字串。例如蚯蚓的队伍为 10 号蚯蚓在队首,其后是 22 号蚯蚓,其后是 3 号蚯蚓(为队尾),这些蚯蚓的长度分别为 4 、 5 、 6 ,则 10 号蚯蚓的向后 3 数字串为 456, 22 号蚯蚓没有向后 3 数字串,但其向后 2 数字串为 56,其向后 1 数字串为 5

而 f(t) 表示所有蚯蚓中,向后 k 数字串恰好为 t 的蚯蚓只数。

输入描述

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

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

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

  • 1 i j(1≤i,j≤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 的操作,输出一行,仅包含一个整数,表示询问的结果。

样例输入

5 9
3 1 3 5 3
3 333135 2
3 333135 1
1 1 3
1 2 5
1 3 2
1 5 4
3 333135 2
3 333135 1
3 333135 3

样例输出

0
81
1
81
0
数据范围与提示

保证 n≤2×105,m≤5×105,k≤50 。

设 ∑∣s∣ 为某个输入文件中所有询问的 s 的长度总和,则 ∑∣s∣≤107 。

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

每个测试点的详细信息见下表:

测试点编号nmk∑∥s∥c全为 1
1=1≤103=1≤103=0No
2≤20≤40≤10≤103=0No
3≤150≤2,000≤50≤103≤103No
4≤500≤600≤50≤103=0No
5≤103≤2,000≤50≤103≤103No
6≤5×104≤6×104≤5≤5×104≤103No
7≤5×104≤6×104≤50≤5×104=0Yes
8≤5×104≤6×104≤50≤5×104=0No
9≤5×104≤6×104≤50≤5×104≤103No
10≤5×104≤8×104≤50≤2.5×106=0No
11≤5×104≤8×104≤50≤2.5×106≤103No
12≤105≤1.1×105≤6≤105≤103No
13≤105≤1.1×105≤50≤105=0Yes
14≤105≤1.1×105≤50≤105=0No
15≤105≤1.1×105≤50≤105≤103No
16≤105≤1.5×105≤50≤5×106=0No
17≤105≤1.5×105≤50≤5×106≤103No
18≤2×105≤5×105=1≤107=0No
19≤2×105≤5×105=1≤107≤103No
20≤2×105≤2.5×105≤7≤2×105≤103No
21≤2×105≤2.5×105≤50≤2×105=0Yes
22≤2×105≤2.5×105≤50≤2×105=0No
23≤2×105≤2.5×105≤50≤2×105≤103No
24≤2×105≤3×105≤50≤107=0No
25≤2×105≤3×105≤50≤107≤103No

如果一个测试点的“全为1”的一列为“Yes”,表示该测试点的所有蚯蚓的长度均为 1,并且所有询问串 s 的每一位也均为1

代码实现:

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    int x = 0, f = 1; char c; c = getchar() ;
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
typedef unsigned long long ull ;
const int maxn = 2e5 + 10, base = 131, mod = 998244353, Hash_mod = 19260817 ;
const int maxs = 1e7 + 10 ;
int a[maxn], nxt[maxn], pre[maxn] ;
ull p[maxn] ;
char str[maxs] ;
struct Hash_table {
    int head[Hash_mod], nxt[maxs], len[maxs], val[maxs], tot ;
    ull s[maxs] ;
    inline void add (ull ss, int l, int v) {
        int tmp = ss % Hash_mod ;
        for (int i = head[tmp]; i; i = nxt[i])
            if (len[i] == l && s[i] == ss) {
                val[i] = (val[i] + v) % mod; return ;
            }
        len[++ tot] = l; val[tot] = v; s[tot] = ss ;
        nxt[tot] = head[tmp]; head[tmp] = tot ;
    }
    inline int query (ull ss, int l) {
        int tmp = ss % Hash_mod ;
        for (int i = head[tmp]; i; i = nxt[i])
            if (len[i] == l && s[i] == ss)
                return val[i] ;
        return 0 ;
    }
}Hash ;
inline void link (int x, int y) {
    nxt[x] = y; pre[y] = x ;
    ull pres = 0; int tot = 0 ;
    for (int i = x, len = 49; i && len; i = pre[i], -- len) {
        pres += a[i] * p[tot ++] ;
        ull s = pres; int l = tot + 1 ;
        for (int j = y; j && l <= 50; j = nxt[j], ++ l) {
            s = s * base + a[j]; Hash.add (s, l, 1) ;
        }
    }
}
inline void cut (int x) {
    int y = nxt[x] ;
    ull pres = 0; int tot = 0 ;
    for (int i = x, len = 49; i && len; i = pre[i], -- len) {
        pres += a[i] * p[tot ++] ;
        ull s = pres; int l = tot + 1 ;
        for (int j = y; j && l <= 50; j = nxt[j], ++ l) {
            s = s * base + a[j]; Hash.add (s, l, mod - 1) ;
        }
    }
    nxt[x] = pre[y] = 0 ;
}
inline int query (int k) {
    ull s = 0 ;
    int len = strlen (str + 1), res = 1 ;
    str[0] = '0' ;
    for (int i = 1; i < k; i ++) s = s * base + str[i] - '0' ;
    for (int i = k; i <= len; i ++) {
        s = s * base + str[i] - '0' ;
        s -= p[k] * (str[i - k] - '0') ;
        res = 1ll * res * Hash.query (s, k) % mod ;
    }
    return res ;
}
int main() {
    int n = read(), m = read() ;
    for (int i = 1; i <= n; i ++) {
        a[i] = read() ;
        Hash.add (a[i], 1, 1) ;
    }
    p[0] = 1 ;
    for (int i = 1; i <= n; i ++) p[i] = p[i - 1] * base ;
    while (m --) {
        int op = read() ;
        if (op == 1) {
            int x = read(), y = read() ;
            link (x, y) ;
        } else if (op == 2) {
            int x = read() ;
            cut (x) ;
        } else {
            scanf("%s", str + 1); int k = read() ;
            printf("%d\n", query (k)) ;
        }
    }
    return 0 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值