bzoj4377

24 篇文章 0 订阅

题意:
给定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不能作为开头,扫的时候顺便减掉就好了。
题解都是不合法区间取并的。。为什么我的方法老比题解的慢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值