BZOJ 4377 - [POI2015]Kurs szybkiego czytania
Published on 2017-01-31给定 ,其中 互质。定义一个长度为 的 串 ,其中 当且仅当 。
给定一个长为 的小 串,求出小串在大串中出现了几次。
分析
首先发现 是不可能相同的。原因是假设 ,那么就有 ,因为 ,所以 存在,两边同乘 得到 ,所以 必然相等。
得到这个性质,可以发现惯常的匹配方法就派不上用场了, 高达 ,所以不可能将 写出来,我们得想别的办法。
设 ,由于 ,只要固定了 ,那么 都是知道的,也就是说只要固定了 ,那么 也是固定的, 是不是一个合法的匹配的开头也就是知道的。
由于 两两不重复,于是我们只需要知道有多少个 可以作为匹配的开头即可。
我们观察模式串 ,假设 作为开头,那么每一位 对于 的值就有一个约束,比如 ,那么有约束 ;再如 ,那么有约束 。所有这些约束的交集,就是所有可以作为开头的 (注意要排除掉 ),对于这种模意义下的不等式,我们考虑区间平移的方法处理,那么每一条约束就有可能变为 或 ,这是不容易求并集的。我们转而考虑计算所有不可行区间的并集,排序后扫描就能得到所有可行的取值区间。
最多有 个不可行区间,总复杂度 。
总结:本题需要观察到普通的匹配方法是没法用的,由于 不重复,所以转而考虑哪些 可以作为开头,构造出 的取值约束,最后转化到了一个经典的区间求并问题上,非常妙。
代码
// Created by Sengxian on 2017/1/29. // Copyright (c) 2017年 Sengxian. All rights reserved. // BZOJ 4377 数学 #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int readInt() { static int n, ch; n = 0, ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar(); return n; } const int MAX_M = 1000000 + 3; int n, a, b, p, m; char s[MAX_M]; typedef pair<int, int> range; range ranges[MAX_M * 4]; int cnt = 0; inline void insert(const range &r) { if (r.second - r.first >= 1) ranges[cnt++] = r; } inline void add(const range &r) { insert(range(0, r.first)); insert(range(r.second, n)); } inline void add(const range &r1, const range &r2) { insert(range(0, r1.first)); insert(range(r1.second, r2.first)); insert(range(r2.second, n)); } int solve() { ranges[cnt++] = range(0, 0), ranges[cnt++] = range(n, n); sort(ranges, ranges + cnt); int ans = 0; for (int i = 0, j = 0; i < cnt; i = j) { int rightMost = ranges[i].second; while (j < cnt && ranges[j].first <= rightMost) rightMost = max(rightMost, ranges[j++].second); if (j != cnt) ans += ranges[j].first - rightMost; } return ans; } int main() { scanf("%d%d%d%d%d%s", &n, &a, &b, &p, &m, s); for (int i = 0, t = 0; i < m; ++i) { if (s[i] == '0') { if (t >= p) add(range(n - t, n + p - t)); else add(range(0, p - t), range(n - t, n)); } else { if (t <= p) add(range(p - t, n - t)); else add(range(0, n - t), range(n + p - t, n)); } (t += a) %= n; } for (int i = n - m + 1; i < n; ++i) ranges[cnt++] = range(((ll)a * i + b) % n, ((ll)a * i + b) % n + 1); printf("%d\n", solve()); return 0; }