Description
对于 k k k进制数 x x x,定义 d ( x ) d(x) d(x)为 x x x的各数位的和的 k k k进制表示,如果结果超过一位,则继续重复执行各数位求和操作,直至结果为 1 1 1位。
比如说,在 7 7 7进制下, d ( 350 4 7 ) = d ( ( 3 + 5 + 0 + 4 ) 7 ) = d ( 1 5 7 ) = d ( ( 1 + 5 ) 7 ) = d ( 6 7 ) = 6 d(3504_7)=d((3+5+0+4)_7)=d(15_7)=d((1+5)_7)=d(6_7)=6 d(35047)=d((3+5+0+4)7)=d(157)=d((1+5)7)=d(67)=6
定义 x x x为幸运的,当且仅当 d ( x ) = b d(x) = b d(x)=b;
现在给定 k k k进制下的 n n n个数位 a 1 a 2 . . . a n a_1 a_2 ...a_n a1a2...an,问其中有多少子串组成的数字是幸运的。
Input
第一行三个整数 k , b , n k,b,n k,b,n
第二行 n n n个整数 a i a_i ai
( 2 ≤ k ≤ 1 0 9 , 0 ≤ b < k , 1 ≤ n ≤ 1 0 5 , 0 ≤ a i < k ) (2\le k\le 10^9,0\le b<k,1\le n\le 10^5,0\le a_i<k) (2≤k≤109,0≤b<k,1≤n≤105,0≤ai<k)
Output
输出一个整数表示幸运的子串数。
Sample Input
10 5 6
3 2 0 5 6 1
Sample Output
5
Solution
显然 d ( x ) = ( x − 1 ) % ( k − 1 ) + 1 d(x)=(x-1)\%(k-1)+1 d(x)=(x−1)%(k−1)+1, d ( x ) = 0 d(x)=0 d(x)=0当且仅当 x x x每位都为 0 0 0,首先统计连续为 0 0 0的段的个数 r e s res res,如果 b = 0 b=0 b=0则答案即为 r e s res res,如果 b = k − 1 b=k-1 b=k−1,为方便统计,直接令 d ( x ) = x % ( k − 1 ) d(x)=x\%(k-1) d(x)=x%(k−1),如此求出的满足 d ( x ) = 0 d(x)=0 d(x)=0的子串本质上是 d ( x ) = 0 , k − 1 d(x)=0,k-1 d(x)=0,k−1的子串数量和,该值减去 r e s res res即为答案
现在考虑求满足 d ( x ) = b d(x)=b d(x)=b的子串个数,令 s i = ( a 1 + . . . + a i ) % ( k − 1 ) s_i=(a_1+...+a_{i})\%(k-1) si=(a1+...+ai)%(k−1),那么以 i i i为右端点的合法区间 [ j , i ] [j,i] [j,i]需要满足 s i − s j − 1 ≡ b ( m o d k − 1 ) s_i-s_{j-1}\equiv b(mod\ k-1) si−sj−1≡b(mod k−1),用 m a p map map统计每种前缀和出现次数即可
Code
#include<cstdio>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=100005;
map<int,int>M;
int k,b,n,a[maxn];
int main()
{
scanf("%d%d%d",&k,&b,&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
ll ans=0;
for(int i=1,pre=1;i<=n;i++)
{
if(a[i])pre=i+1;
else ans+=i-pre+1;
}
if(b==0)
{
printf("%lld\n",ans);
return 0;
}
if(b==k-1)ans=-ans,b=0;
else ans=0;
M[0]=1;
for(int i=1,res=0;i<=n;i++)
{
res=(res+a[i])%(k-1);
ans+=M[(res-b+k-1)%(k-1)];
M[res]++;
}
printf("%lld\n",ans);
return 0;
}