題目鏈接
http://www.lydsy.com/JudgeOnline/problem.php?id=4377
= =
題意
給定n,a,b,p,其中n,a互質。定義一個長度爲n的01串c[0..n-1],其中c[i]==0當且僅當(ai+b) mod n < p。
給定一個長爲m的小01串,求出小串在大串中出現了幾次。
思路
一開始自己想了想,只是看出了n,a互質保證了所有數只會出現一次(這個不知道爲什麼的自己看看相關數論把……),然後想了半天沒想出來(太菜了QAQ),看了網上的題解也不是特別懂,後來問了gtw,他一說我就明白了,果然是數論大神 %%%
和網上其他題解一樣,如果知道了一個位置的數字,那麼它前後位置的數字都可以推出,又根據m串給出的大小關係(0,1代表了題中不等輸的小於和大於等於),我們可以確定滿足條件起點的取值範圍(記得去掉後面幾個長度沒有m的子串),求出答案。
同時考慮,如果求答案可能存在的範圍,就要對所有集合求交集,複雜度太高,於是我們求答案不可能存在的範圍,對所有範圍求並集。
對應的不等式:
0 -> [p - now, n - 1 - now] mod n
1 -> [0 - now, p - 1 - now] mod n
now是a的不斷累加結果,因爲我們要確定第一個數的取值,所以我們把當前的範圍減一個now。
又根據模域的性質,如果l > r的話,就取[0, r]和[l, n - 1]。
代碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
const int MAXN = 1e6 + 5;
int n, a, b, p, m;
char str[MAXN];
P range[MAXN * 3];
int rancnt;
void addrange(int l, int r) {
if (l <= r) {
range[rancnt].first = l;
range[rancnt++].second = r;
} else {
range[rancnt].first = l;
range[rancnt++].second = n - 1;
range[rancnt].first = 0;
range[rancnt++].second = r;
}
}
int main() {
while (~scanf("%d%d%d%d%d%s", &n, &a, &b, &p, &m, str)) {
rancnt = 0;
for (int i = 0, now = 0; i < m; ++i, (now += a) %= n) {
if (str[i] == '0') addrange((p - now + n) % n, (n - 1 - now + n) % n);
else addrange((n - now) % n, (p - 1 - now + n) % n);
}
for (int i = 0, now = (b + n - a) % n; i < m; ++i, (now += n - a) %= n) {
addrange(now, now);
}
sort(range, range + rancnt);
int ans = 0, head = range[0].first, tail = range[0].second;
for (int i = 1; i < rancnt; ++i) {
if (tail < range[i].first) {
ans += tail - head + 1;
head = range[i].first;
tail = range[i].second;
} else {
tail = max(tail, range[i].second);
}
}
ans += tail - head + 1;
printf("%d\n", n - ans);
}
}