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);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值