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;
}