Luogu3823 NOI2017 蚯蚓排队

9 篇文章 0 订阅
3 篇文章 0 订阅

题面luogu3823

题意 n n n只蚯蚓,每只蚯蚓有一个 ≤ 6 \le 6 6的长度,初始时每只蚯蚓一支队伍。给出 m m m个操作。

三种操作:

  1. 将以 i i i结尾的队伍和以 j j j开头的队伍合并,且 i i i的队伍在前。
  2. i i i i i i后面一只蚯蚓处断开,分为两支队伍。
  3. 定义以第 x x x只蚯蚓开始的长度为 k k k数字串为:从 x x x开始的连续 k k k只蚯蚓,它们的长度数字连成的字符串。定义给定一个字符串 s s s和整数 k k k s s s的每个长度为 k k k的子串 s ′ s' s,定义 f ( s ′ ) f(s') f(s)为有多少只蚯蚓开头的长度为 k k k的数字串等于 s ′ s' s。询问 ∏ s ′ f ( s ′ ) \prod _{s'} f(s') sf(s)

n ≤ 2 × 1 0 5 n \le 2 \times 10^5 n2×105 m ≤ 3 × 1 0 5 m \le 3 \times 10^5 m3×105 k ≤ 50 k \le 50 k50 ∑ ∣ s ∣ ≤ 1 0 7 \sum _{|s|} \le 10^7 s107

题解:首先对于队伍的操作,用双向链表即可维护。

对于数字串,可以用哈希维护。

观察到 k k k的范围很小,意味着每次队伍发生合并或断开时,只会影响到 k k k只蚯蚓的数字串哈希值。

因此每次暴力更新哈希值的复杂度为 O ( k 2 ) O(k^2) O(k2)

建一张哈希表,关键字为哈希值。查询时直接查哈希表即可。

#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 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值