题意:
给定n,a,b,p,其中n,a互质。定义一个长度为n的01串c[0..n-1],其中c[i]==0当且仅当(ai+b) mod n < p。给定一个长为m的小01串,求出小串在大串中出现了几次。
2<=n<=10^9,1<=p,a,b,m< n,1<=m<= 10^6。
n和a互质。
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 3100000
#define LL long long
using namespace std;
struct node{int x,o;}A[N];
int n,m,a,b,c[N],num,p,cnt,q[N],ans,ql;
char s[N];
bool cmp(node x,node y)
{
if(x.x<y.x) return 1;
return 0;
}
int main()
{
scanf("%d%d%d%d%d",&n,&a,&b,&p,&m);
scanf("%s",s);
for(int i=0;i<m;i++)
if(s[i]=='1') c[i]=1;
for(int i=0;i<m;i++)
{
int l,r;
if(c[i]==0) l=0,r=p-1;
else l=p,r=n-1;
l=(((LL)l-(LL)i*a)%(LL)n+n)%n;
r=(((LL)r-(LL)i*a)%(LL)n+n)%n;
if(l<=r)
{
A[++num].x=l;A[num].o=1;
A[++num].x=r+1;A[num].o=-1;
}
else
{
A[++num].x=0;A[num].o=1;
A[++num].x=r+1;A[num].o=-1;
A[++num].x=l;A[num].o=1;
}
}
for(int i=n-m+1;i<n;i++) q[++ql]=((LL)i*a+(LL)b)%(LL)n;
sort(A+1,A+num+1,cmp);
sort(q+1,q+ql+1);
int i=1,j=1;
while(i<=num)
{
int st=i;
while(j<=ql && q[j]<A[st].x) j++;
while(i<=num && A[i].x==A[st].x) {cnt+=A[i].o;i++;}
if(cnt==m)
{
if(i<=num)
{
ans+=A[i].x-A[st].x;
while(j<=ql && q[j]<A[i].x)
{ans--;j++;}
}
else
{
ans+=n-A[st].x;
while(j<=ql) {ans--;j++;}
}
}
}
printf("%d\n",ans);
return 0;
}
题解:
这题我去年连题解都看不懂
由于n和a互质,所以(ia+b)%n对应了[0,n-1]的每个值。
我们考虑和小串第一个位置匹配的(ia+b)%n=k
对于小串这一位是0或1,k有确定的左右界。即可以列出l1<=k<=r1
既然(ia+b)%n第一步落在了k,第二步必定落在(k+a)%n。于是有l2<=(k+a)%n<=r2。
同理对每个位置都列一条方程。于是解出了O(m)个区间(解的l可能大于r,表示[l,n-1]或[0,r]。这时拆成两个区间即可),一个k可以作为合法匹配的开头当且仅当他被m个区间覆盖,用扫描线处理。注意最后的m-1个i的(ia+b)%n不能作为开头,扫的时候顺便减掉就好了。
题解都是不合法区间取并的。。为什么我的方法老比题解的慢