BZOJ4377[POI2015]Kurs szybkiego czytania——数学思维题
题目描述
给定n,a,b,p,其中n,a互质。定义一个长度为n的01串c[0..n-1],其中c[i]==0当且仅当(ai+b) mod n < p。
给定一个长为m的小01串,求出小串在大串中出现了几次。
输入
第一行包含整数n,a,b,p,m(2<=n<=10^9,1<=p,a,b,m<n,1<=m<=10^6)。n和a互质。
第二行一个长度为m的01串。
输出
一个整数,表示小串在大串中出现了几次
样例输入
9 5 6 4 3
101
101
样例输出
3
提示
假设我们以长串中第i个作为匹配开头,(ai+b)%n=x,那么接下来长串中的字符的表达式就是x+a、x+2a、x+3a……。
如果以i为开头能匹配成功,那么假设短串是0110,就要满足0<=x<p;p<=x+a<n;p<=x+2a<n;0<=x+3a<p(不考虑取模)。
这样就能列出m个不等式,这m个不等式的交集就是以x为开头能成功匹配的x的取值范围。
因为n,a互质,所以不存在两个位置的表达式值相同,x取值范围的区间长度就是答案。
但取模之后就可能将每个不等式的一个取值范围变成开头和结尾的两个,不好求答案。
因此可以找到每个不等式不成立的取值范围,将这些范围取并集,他们的补集就是答案。
将所有不成立区间按左端点排个序,从左到右扫一遍即可。
注:代码中实际是以ai+b中的ai为未知变量,因为ai+b与ai的取值范围长度相同,所以不影响答案。
#include<set> #include<map> #include<cmath> #include<queue> #include<stack> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct miku { int l; int r; }f[4000010]; int cnt; int n,p,a,b,m; char s[1000010]; int now; int ans; void add(int x,int y) { cnt++; f[cnt].l=x; f[cnt].r=y; } void updata(int a,int b,int c,int d) { if(a) { add(0,a); } if(b<c) { add(b,c); } if(d<n) { add(d,n); } } bool cmp(miku a,miku b) { return a.l<b.l; } int main() { scanf("%d%d%d%d%d",&n,&a,&b,&p,&m); scanf("%s",s); for(int i=0;i<m;i++,b=(b+a)%n) { if(s[i]=='0') { updata(0,max(0,p-b),n-b,min(n,n+p-b)); } else { updata(max(p-b,0),n-b,min(n,p+n-b),n); } } for(int i=n-1,b=n-a;i>=n-m+1;b=(b-a+n)%n,i--) { add(b,b+1); } add(n,n+1); sort(f+1,f+1+cnt,cmp); for(int i=1;i<=cnt;i++) { if(f[i].l>now) { ans+=f[i].l-now; } if(f[i].r>now) { now=f[i].r; } } printf("%d",ans); }