题意
给你一个长度为 n
的 01
序列,每一个数位是 0
当且仅当 (a*i+b) mod n < p
。求长度为 m
的 01
串出现了几次。
Solution:
数论+集合求交。
首先考虑互质,所以 a*i
互不相同。如果我们枚举序列开头 x
,可以得到 m-1
个关于 a*i
模 p
意义下的不等式,又因为 a*i%p
和 i%p
对应,所以求出方程的解集即可。
考虑怎么解模 p
的不等式 :
0<=x+delta<=p-1 <=> -delta<=x<=p-1-delta <=> [l,r] (l<=r) 或 [0,r],[l,n-1] (l>r)
注意要把两边转成非负数,并且分类讨论。(这个当成结论记,毕竟没有严格证明)。
最后维护求交即可。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define PII pair<ll,int>
#define All(a) a.begin(),a.end()
using namespace std;
const int mx=4e6+5;
struct node{
int l,r;
bool operator <(const node &a)const {
return l<a.l||l==a.l&&r<a.r;
}
}t[mx];
int n,a,b,p,m,tot;
char op[mx];
int main() {
scanf("%d%d%d%d%d",&n,&a,&b,&p,&m);
scanf("%s",op);
//这个模数解方程很恶心
for(int i=0,j=0;i<m;i++) {
int delta=(1ll*i*a+b)%n;
int x,y;
//结论是反的
if(op[i]=='1') {
//0<=x+delta<=p-1
//-delta<=x<=p-1-delta
x=(-delta+n)%n;
y=(p-1-delta+n)%n;
}
else {
//p<=x+delta<=n-1
//p-delta<=x<=n-1-delta
x=(p-delta+n)%n;
y=(n-1-delta+n)%n;
}
//分类讨论解方程
if(x<=y) {
t[++tot].l=x,t[tot].r=y;
}
else {
t[++tot].l=0,t[tot].r=y,t[++tot].l=x,t[tot].r=n-1;
}
}
//扫一遍 (n-m,n)
for(int i=n-m+1;i<n;i++) {
int x=1ll*a*i%n;
t[++tot].l=x,t[tot].r=x;
}
//t[] 求并集:
//1. 先按左端点排序,然后按右端点合并
sort(t+1,t+1+tot);
// for(int i=1;i<=tot;i++) {
// printf("%d %d\n",t[i].l,t[i].r);
// }
int l=t[1].l,r=t[1].r,tmp=0; //细节问题
//统计交集点的个数
for(int i=2;i<=tot;i++) {
if(t[i].l<=r) r=max(r,t[i].r);
else {
tmp+=r-l+1;
l=t[i].l,r=t[i].r;
}
}
if(l<=r) {
tmp+=r-l+1;
}
printf("%d",n-tmp);
}