题解 [HNOI2016]大数
题目大意
给出一个\(n\)个数的字符串,有\(m\)次查询,对于该串的子串\([l,r]\)有多少个子串满足是固定素数\(p\)的倍数。
思路
其实很简单,但是一开始想偏了。。。果然还是自己菜啊。。。
我们可以想到统计一下后缀和\(s[i]\),表示\([i,n]\)构成的数,那么,判断一个区间\([l,r]\)是不是\(p\)的倍数就等价于:
\[\dfrac{s[l]-s[r+1]}{10^{n-r}}\equiv 0 \pmod p
\]
我们发现如果\(\gcd(10,p)=1\)的话,分母就不会产生影响,于是判断条件就是:
\[s[l]\equiv s[r+1]\pmod p
\]
于是对于这种情况我们就可以用莫队\(\Theta(n\sqrt n)\)开桶记录答案。
如果\(\gcd(10,p)\not=1\)的话,那么\(p=2 \operatorname{or} 5\),我们发现这种情况对于区间\([l,r]\)判断是否的话直接判断第\(r\)位是不是\(2\operatorname{or}5\)的倍数即可。于是我们可以\(\Theta(n)\)解决这种情况。
果然还是自己菜了啊。。。这都没有看出来。。。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define ll long long
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
char S[MAXN];
ll sum,ans[MAXN];int n,m,p,un,s[MAXN],tmp[MAXN],bel[MAXN],cnt[MAXN];
namespace Subtask1{
struct node{
int l,r,id;
bool operator < (const node &p)const{return bel[l] != bel[p.l] ? l < p.l : r < p.r;}
}q[MAXN];
void cge (int x,int k){sum -= 1ll * cnt[x] * (cnt[x] - 1) / 2,cnt[x] += k,sum += 1ll * cnt[x] * (cnt[x] - 1) / 2;}
void Work (){
int siz = sqrt (n);
for (Int i = n,c = 1;i;-- i,c = 1ll * c * 10 % p) s[i] = (s[i + 1] + 1ll * c * (S[i] - '0') % p) % p,bel[i] = (i - 1) / siz + 1,tmp[i] = s[i];
sort (tmp + 1,tmp + n + 2);un = unique (tmp + 1,tmp + n + 2) - tmp - 1;for (Int i = 1;i <= n + 1;++ i) s[i] = lower_bound (tmp + 1,tmp + un + 1,s[i]) - tmp;
read (m);for (Int i = 1;i <= m;++ i) read (q[i].l,q[i].r),q[i].r ++,q[i].id = i;sort (q + 1,q + m + 1);int l = 1,r = 0;
for (Int i = 1;i <= m;++ i){
while (l < q[i].l) cge (s[l ++],-1);while (l > q[i].l) cge (s[-- l],1);
while (r < q[i].r) cge (s[++ r],1);while (r > q[i].r) cge (s[r --],-1);
ans[q[i].id] = sum;
}
for (Int i = 1;i <= m;++ i) write (ans[i]),putchar ('\n');
return ;
}
}
namespace Subtask2{
int snum[MAXN];ll ssum[MAXN];
void Work(){
for (Int i = 1;i <= n;++ i){
snum[i] = ((S[i] - '0') % p == 0);
ssum[i] = ssum[i - 1] + 1ll * ((S[i] - '0') % p == 0) * i;
}
read (m);
while (m --){
int l,r;read (l,r);
write (ssum[r] - ssum[l - 1] - (snum[r] - snum[l - 1]) * (l - 1)),putchar ('\n');
}
}
}
signed main(){
read (p),scanf("%s",S + 1),n = strlen (S + 1);
if (p == 2 || p == 5) Subtask2::Work ();
else Subtask1::Work ();
return 0;
}