题目大意:
给出 p , p是一个很大的数,其位数在10^5位以内,定义S( t )为一个整数中去任意的连续 i 位得到的数的和, i 从1到 t 的长度
比如 t = 1025时, S(1025) = 1 + 0 + 2 + 5 + 10 + 02 + 25 + 102 + 025 + 1025 = 1575
定义F( t , k ) 为将 t 重复写 k 次得到的新的数, 比如 F( 1025 , 3 ) = 102510251025
现在要求求出 S( F( p , k ) )
输入给出 p, k ,m 要求输出 S( F( p , k ) ) mod m;
大致思路:
首先这道题求出 S ( F( p , k ) ) 的通项是不方便取模的,这里写出我的计算过程:
先考虑没有 k 次重复的情况, 对于一个数 p ,先来求 S( p )
将数 p 用字符串储存之后得到每一位的数字
设s[1] ~ s[len] 依次是数 p 的最低位到最高位, len 是字符串 p 的长度
那么 对于从低到高的第 i 位的数 s[ i ] 在和 S( p ) 中出现的情况如下:
s[1]作为个位出现 len 次
s[2] 作为个位出现 len - 1 次, 作为十位出现 len - 1次
s[3] 作为个位出现 len - 2次, 作为十位出现 len - 2 次, 作为百位出现 len - 3次
.....
这个很容易证明,这里不给出证明,自己画一下就明白了
s[ i ] 作为个位出现 len - i + 1次,作为十位出现 len - i + 1次....作为数级是 10^(i - 1) 的位出现 len - i + 1次
那么对于数 p , 设其长度为 len , 从最低位到最高位的数依次是 s[1] ~ s[len]
那么:
那么这里到 p 重复 k 次时的情况便是 len 变为k*len而 p 这个数变成重复k次得到的数
那么便有 s[ i + c*len ] = s[ i ], c 从0到 k - 1
然后由于本题中数据范围很大,最后的结果是取模 m 输出
那么由于分母有一个9, 取的模并不保证是一个质数,也就不能使用费马小定理转换
注意到分子一定能被9整除,可以发现这样一个事实:
对于整数A有 A mod B = ((9*A) mod (9*B)) / 9;
这是一个很容易证明的结论,证明如下:
设 A = a*B +b;
那么 A mod B = b
9*A mod(9*B) = (9*a*B +9*b) mod (9*B) = 9*b
所以 A mod B = ((9*A) mod (9*B)) / 9;
也就是说 对于S(p) % m, 我们可以求 (S(p) * 9) % (9*m) 将最后的结果除以9即可
那么我们现在可以不考虑分母9的问题,但是这样做会造成一个隐患,由于现在取模m 变成了取模 9*m, 对于两个小于 9*m的数相乘范围在 1.8*10^19内,超出了long long 的范围, 这样便不能直接将两个long long型在9*m范围内的数直接相乘取模,这里用到一个方法,代码和快速幂相似,成功避免了两个数直接乘超出数据范围的情况,主要思想是将一个long long拆成多个数来一次相乘将得到的结果相加取模,避免溢出,具体见代码中的 mul 函数部分
那么继续将这个公式推至S( F( p, k) ) 的形式:
现在来看最后的这个式子,变量 i 从1到 len 可以直接循环 (len <= 100000)
剩下的就是两个求和式的问题,对于sigma(base^i) i 从0 到 k-1 可以每次二分来求
用Geo(base, pow) 表示 sigma(base^i) i 从0 到pow - 1 有
Geo(base, 0) = 1;
Geo(base, pow) = Geo(base, pow - 1) + base^(pow - 1) (pow 是奇数时)
Geo(base, pow) = Geo(base, pow / 2) * ( 1 + base^(pow / 2)) (pow 是偶数时)
这个做法在之前写的 Codeforces 327C Magic Five那道题求等比数列的和的时候也用到过
那么对于sigma( i * base^i ) i 从0 到 pow - 1来说也可以用二分来求
用AriGeo(base, pow) 表示sigma( i * base^i ) i 从0 到pow - 1有:
AriGeo(base, 0) = 0;
AriGeo(base, pow) = AriGeo(base, pow - 1) + (pow - 1)*base^(pow - 1) (pow 是奇数时)
AriGeo(base, pow) = AriGeo(base, pow ./ 2)(1 + base^(pow./ 2)) + (pow / 2) * (base^(pow / 2)) * Geo(base, pow / 2) (pow 是偶数时)
上面这个式子可以自己验证一下
对于每一个求幂的式子,都用快速幂的方法来求
这样整个复杂度便降下来了,注意在减法操作之后加上要取模的数之后取模,防止负数出现
整个思路和注意的要点都已经说完了,接下来是代码:
Result : Accepted Memory : 2021 KB Time : 875 ms
/*
* Author: Gatevin
* Created Time: 2014/7/27 11:10:56
* File Name: test.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
char ss[100010];
lint s[100010];
lint k,m,mod,len;
lint ten[100010];
lint mul(lint a, lint b)
{
lint ret = 0;
while(b)
{
if(b & 1)
{
ret = (ret + a) % mod;
}
b >>= 1;
a= (a*2) % mod;
}
return ret;
}
lint quick_pow(lint base, lint pow)
{
lint ret = 1;
while(pow)
{
if(pow & 1)
{
ret = mul(ret, base);
}
pow >>= 1;
base = mul(base, base);
}
return ret;
}
//Evaluate the sum of geometric sequence sigma(base^i) , i range from 0 to pow - 1
lint GeoSeq(lint base, lint pow)
{
if(pow == 1) return 1;
if(pow & 1) return (GeoSeq(base, pow - 1) + quick_pow(base, pow - 1)) % mod;
else return mul(GeoSeq(base, pow >> 1), (1 + quick_pow(base, pow >> 1)) % mod);
}
/*
* Evaluate the sum of the sequence which is a combination of
* an arithmetic sequence and a geomatric sequence sigma(i*base^i), i range from 0 to pow - 1
*/
lint AriGeoSeq(lint base, lint pow)
{
if(pow == 1) return 0;
if(pow & 1) return (AriGeoSeq(base, pow - 1) + mul(pow - 1, quick_pow(base, pow - 1))) % mod;
else return (mul(AriGeoSeq(base, pow >> 1), 1 + quick_pow(base, pow >> 1))
+ mul(mul(pow >> 1, quick_pow(base, pow >> 1)), GeoSeq(base, pow >> 1))) % mod;
}
lint solve()
{
lint answer = 0;
ten[0] = 1;
for(int i = 1; i <= len; i++)
{
ten[i] = (ten[i - 1] * 10) % mod;
}
lint tmp1 = GeoSeq(ten[len], k);
lint tmp2 = AriGeoSeq(ten[len], k);
lint tmp3 = (k*(k - 1)/2) % mod;
for(int i = 1; i <= len; i++)
{
answer = (answer + mul(s[i], (mul(mul(tmp1, (k*len - i + 1) % mod), ten[i]) - mul((k*len - i + 1) % mod, k)
- mul(mul(tmp2, len), ten[i]) + mul(len, tmp3) + 2*mod) % mod)) % mod;
}
return answer;
}
int main()
{
scanf("%s",ss);
scanf("%I64d%I64d",&k,&m);
mod = 9*m;
len = strlen(ss);
for(int i = 0; i <= len - 1; i++)
{
s[len - i] = ss[i] - '0';
}
lint answer = solve();
cout<<answer/9<<endl;
return 0;
}