bzoj 4377 Kurs szybkiego czytania 數學思維題

題目鏈接

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);
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章