BZOJ 4542 HNOI2016 大数

13 篇文章 0 订阅
4 篇文章 0 订阅

Problem

BZOJ

Solution

我一看题:不会做
旁边的大佬一看题:(惊呼)这难道又是莫队?
Orz我果然还是太弱了

这个题目只需要将问题转化一下。
比如对于模数p=11,有字符串1213。我们用后缀和(因为字符串是倒着的啊)的思想,则有suf[4]=3,suf[1]=1213,而(suf[1]-suf[4])%11==0则表明字符串[1,3]是可以被11整除的。那么我们不妨对suf数组进行取模运算,我们可以得到类似的结论(suf[1]%11-suf[4]%11)%11==0则表明整除。
那么这个问题就变成了一个区间询问相同数对的对数的问题。也就是一道莫队的模板题。

另外,10并不是一个质数,所以当模数为2或5的时候,特判处理一下即可。比较容易处理,因为只需要尾数能被2或5整除那么就可以被2或5整除。所以它在第i位,以它结尾的整除的就有i个,然后前缀和一下,询问时再减去开头位置超过ql而导致不合法的个数(ql-1)*(cnt[qr]-cnt[ql-1])。

值得注意的是,因为在统计答案的时候r可能会超过n,比如之前的那个例子,要统计1213的答案时,需要判断(suf[1]%11-suf[5]%11)%11。所以读入之后,在字符串尾再加入一个’0’。

Code

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn=100010;
struct query{
    int l,r,pos,id;
    bool operator < (const query &x)const
    {
        if(pos==x.pos) return r<x.r;
        return pos<x.pos;
    }
}q[maxn];
int n,m,p,sqr,tot,l,r,suf[maxn],cnt[maxn],a[maxn],t[maxn];
ll tmp,ans[maxn];
char s[maxn];
template <typename Tp> inline void read(Tp &x)
{
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void work()
{
    for(int i=1;i<=n;i++)
      if((s[i]-'0')%p==0)
        cnt[i]=1,suf[i]=i;
    for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1],suf[i]+=suf[i-1];
    for(int i=1,ql,qr;i<=m;i++)
    {
        read(ql);read(qr);
        printf("%lld\n",(ll)suf[qr]-suf[ql-1]-(ll)(ql-1)*(cnt[qr]-cnt[ql-1]));
    }
}
int input()
{
    read(p);scanf("%s",s+1);read(m);
    n=strlen(s+1);sqr=(int)sqrt(n);
    if(p==2||p==5){work();return 0;}
    s[++n]='0';
    for(int i=1;i<=m;i++)
    {
        read(q[i].l);read(q[i].r);++q[i].r;
        q[i].pos=(q[i].l-1)/sqr+1;q[i].id=i;
    }
    sort(q+1,q+m+1);
    ll fac=1;
    for(int i=n;i>=1;i--)
    {
        suf[i]=((ll)(s[i]-'0')*fac%p+suf[i+1])%p;
        a[i]=suf[i];
        fac=fac*10%p;
    }
    sort(a+1,a+n+1);
    tot=unique(a+1,a+n+1)-a-1;
    for(int i=1;i<=n;i++) suf[i]=lower_bound(a+1,a+tot+1,suf[i])-a;
    return 1;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    if(!input()) return 0;
    l=1;r=0;tmp=0;
    for(int i=1;i<=m;i++)
    {
        while(r<q[i].r) tmp+=t[suf[++r]],t[suf[r]]++;
        while(l>q[i].l) tmp+=t[suf[--l]],t[suf[l]]++;
        while(r>q[i].r) t[suf[r]]--,tmp-=t[suf[r--]];
        while(l<q[i].l) t[suf[l]]--,tmp-=t[suf[l++]];
        ans[q[i].id]=tmp;
    }
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值