题目大意:
给出 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; }