[NOI 2017]蚯蚓排队

Description

题库链接

蚯蚓幼儿园有 $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 \leqslant 2 \times 10^5, m \leqslant 5 \times 10^5, k \leqslant 50$ 。设 $\sum |S|$ 为某个输入文件中所有询问的 s 的长度总和,则 $\sum |S| \leqslant 10^7$ 。 设 $c$ 为某个输入文件中形如 2 i 的操作的次数,则 $c \leqslant 10^3$ 。

Solution

由于 $k$ 比较小,直接 $hash$ 就行了。复杂度是正确的,因为整个字符串需被 $hash$ 的子串是 $O(nk)$ 的。由于 $c\leq 10^3$ ,所以修改的字符串复杂度是 $O(ck^2)$ 的。

总复杂度是 $O\left(nk+ck^2+\sum|S|\right)$ 。

Code

#include <bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int N = 2e5+5, LEN = 1e7+5, yzh = 998244353, p = 12456791, base = 31;
void gi(int &x) {
    char ch = getchar(); x = 0;
    for (; ch < '0' || ch > '9'; ch = getchar());
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x<<1)+(x<<3)+ch-48;
}

char ch[LEN];
int n, m, l[N], lst[N], nxt[N], opt, x, y, k;
ull bin[N], tmp;
struct hash_table {
    int cnt[p]; ull ky[p];
    void insert(ull x, int val) {
        int loc = x%p;
        while (true) {
            if (ky[loc] == 0 || ky[loc] == x) {cnt[loc] += val, ky[loc] = x; break; }
            ++loc; if (loc == p) loc = 0;
        }
    }
    int count(ull x) {
        int loc = x%p;
        while (true) {
            if (ky[loc] == 0 || ky[loc] == x) return cnt[loc];
            ++loc; if (loc == p) loc = 0;
        }
    }
}mp;

void merge() {
    int t = 48, loc = x;
    while (t-- && lst[loc]) loc = lst[loc];
    lst[y] = x, nxt[x] = y;
    for (; lst[loc] != x; loc = nxt[loc]) {
        bool flag = 0; t = loc; tmp = 0;
        for (int j = 1; j <= 50 && t; j++, t = nxt[t]) {
            tmp = tmp*base+l[t];
            if (flag) mp.insert(tmp, 1);
            if (t == x) flag = 1;
        }
    }
}
void split() {
    int t = 48, loc = x; y = nxt[x];
    while (t-- && lst[loc]) loc = lst[loc];
    for (; lst[loc] != x; loc = nxt[loc]) {
        bool flag = 0; t = loc; tmp = 0;
        for (int j = 1; j <= 50 && t; j++, t = nxt[t]) {
            tmp = tmp*base+l[t];
            if (flag) mp.insert(tmp, -1);
            if (t == x) flag = 1;
        }
    }
    lst[y] = nxt[x] = 0;
}
int query() {
    int n = strlen(ch+1), ans = 1; tmp = 0;
    for (int i = 1; i <= n; i++) {
        tmp = tmp*base+ch[i]-48;
        if (i > k) tmp -= 1ll*(ch[i-k]-48)*bin[k];
        if (i >= k) ans = 1ll*ans*mp.count(tmp)%yzh;
    }
    return ans;
}
void work() {
    gi(n), gi(m); bin[0] = 1;
    for (int i = 1; i <= n; i++) gi(l[i]), mp.insert(l[i], 1);
    for (int i = 1; i <= 50; i++) bin[i] = bin[i-1]*base;
    while (m--) {
        gi(opt);
        if (opt == 1) gi(x), gi(y), merge();
        else if (opt == 2) gi(x), split();
        else scanf("%s", ch+1), gi(k), printf("%d\n", query());
    }
}
int main() {
    freopen("queue.in", "r", stdin);
    freopen("queue.out", "w", stdout);
    work(); return 0;
}
posted @ 2018-07-09 21:33  NaVi_Awson  阅读(299)  评论(0编辑  收藏  举报