bzoj 4542: [Hnoi2016]大数

Description

小 B 有一个很大的数 S,长度达到了 N 位;这个数可以看成是一个串,它可能有前导 0,例如00009312345
小B还有一个素数P。现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也
是P 的倍数)。例如 S为0077时,其子串 007有6个子串:0,0,7,00,07,007;显然0077的子串007有6个子串都是素
数7的倍数。

solution

正解:莫队
知道结论还是比较容易的,但是细节贼多啊.
首先 \(p\) 是质数,所以满足结论:如果 \(s[i-n]\%p=s[k-n]\%p\),那么 \(s[i-k]\) 满足是 \(p\) 的倍数
第一个细节就是,特判 \(p=2,p=5\),这个时候结论不成立
然后就变成了查找区间内相同的数两两配对的方案数,莫队解决即可,另外这个模数没有给范围,实际上很大,需要离散化

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=100005;
int n,m,block;char s[N];ll mod;
namespace solve1{
  ll sum[N],c[N];
  void main(){
    for(int i=1;i<=n;i++){
      sum[i]=sum[i-1],c[i]=c[i-1];
      if((s[i]-'0')%mod==0)sum[i]+=i,c[i]++;
    }
    int m,x,y;cin>>m;
    while(m--){
      scanf("%d%d",&x,&y);
      printf("%lld\n",(sum[y]-sum[x-1])-(x-1)*(c[y]-c[x-1]));
    }
  }
}
struct node{
  int l,r,b,id;
  bool operator <(const node &pr)const{
    if(b!=pr.b)return b<pr.b;
    return r<pr.r;
  }
}q[N];
ll v[N],t[N],b[N];ll ans[N],ret=0;
inline void add(int x){ret+=t[v[x]];t[v[x]]++;}
inline void delet(int x){t[v[x]]--;ret-=t[v[x]];}
void solve(){
  scanf("%d",&m);
  for(int i=1;i<=m;i++){
    scanf("%d%d",&q[i].l,&q[i].r);
    q[i].r++;q[i].id=i;
    q[i].b=q[i].l/block;
  }
  sort(q+1,q+m+1);
  ll str=0,tot=1;
  for(int i=n;i>=1;i--){
    str+=tot*(s[i]-48);str%=mod;
    tot*=10;tot%=mod;
    v[i]=b[i]=str;
  }
  b[0]=0;
  sort(b,b+n+1);
  int to=unique(b,b+n+1)-b-1;
  for(int i=1;i<=n+1;i++)v[i]=lower_bound(b,b+to+1,v[i])-b;
  int l=1,r=0;
  for(int i=1;i<=m;i++){
    while(r<q[i].r)r++,add(r);
    while(l>q[i].l)l--,add(l);
    while(r>q[i].r)delet(r),r--;
    while(l<q[i].l)delet(l),l++;
    ans[q[i].id]=ret;
  }
  for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
}
void work()
{
  scanf("%lld%s",&mod,s+1);
  n=strlen(s+1);block=sqrt(n);
  if(mod==2 || mod==5)solve1::main();
  else solve();
}

int main()
{
  work();
  return 0;
}

posted @ 2017-12-03 00:53  PIPIBoss  阅读(138)  评论(0编辑  收藏  举报