Codeforces 327C 乘法逆元 + 费马小定理 || 等比数列二分求和取模

8 篇文章 0 订阅
2 篇文章 0 订阅
C. Magic Five
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

There is a long plate s containingn digits. Iahub wants to delete some digits (possibly none, but he is not allowed to delete all the digits) to form his "magic number" on the plate, a number that is divisible by5. Note that, the resulting number may contain leading zeros.

Now Iahub wants to count the number of ways he can obtain magic number, modulo1000000007 (109 + 7). Two ways are different, if the set of deleted positions ins differs.

Look at the input part of the statement, s is given in a special form.

Input

In the first line you're given a string a (1 ≤ |a| ≤ 105), containing digits only. In the second line you're given an integerk (1 ≤ k ≤ 109). The plates is formed by concatenating k copies of a together. That isn = |ak.

Output

Print a single integer — the required number of ways modulo1000000007 (109 + 7).

Examples
Input
1256
1
Output
4
Input
13990
2
Output
528
Input
555
2
Output
63
Note

In the first case, there are four possible ways to make a number that is divisible by 5: 5, 15, 25 and 125.

In the second case, remember to concatenate the copies ofa. The actual plate is 1399013990.

In the third case, except deleting all digits, any choice will do. Therefore there are26 - 1 = 63 possible ways to delete digits.



Well, 这道题题意是要你求一个重复n次的字符串删掉一些数字后组成的数字能被5整除的个数。那么我们可以这么想,能被5整除的数对10取余必定是0 或者 5,也就是说,删除一些数字后,个位一定是0或者5。 那么我们扫一遍,每当str[i] == '0' || str[i] == '5' 时 我们算一算有多少种做法。

对于重复一次的字符串的每一个等于 0 或者 5 的位置来说,有2^(i - 1)(ps: 字符串 [1, len])种删法(删0个,删1个,删2个...... 删 i - 1 个)

那对于重复n次的呢?

有sum[i] = 2^(i - 1) + 2 ^ (i - 1 + len) + 2 ^ (i - 1 + 2 * len)  + ...... + 2 ^ (i - 1 + (n - 1) * len)

令 A = 2 ^ (i - 1),  q = 2 ^ len,上面就等于sum[i] = A * q ^ 0 + A * q ^ 1 + ....... + A * q ^ (n - 1)

===>   sum[i] = A * (q ^ 0 + q ^ 1 + ... + q ^ (n - 1)) = A * (q ^ n     -   1) / (q - 1)

等比数列。那么答案就是所有sum[i]的和。


下面说两种方法解决这种题:乘法逆元 + 费马小定理   &&   等比数列二分求和取余


乘法逆元 + 费马小定理:

关于取余运算不用多说,所有你觉得可能超过MOD的加法乘法都加上取余就行了(a + b) % c = (a % c + b % c) % c, (a * b) % c = (a % c * b % c) % c

肯定有人说直接用等比数列求和公式就行了啊。。。那行吧,别往下看了。

因为公式的计算会使中间结果超int 甚至 long long 范围,会错。

求 A * (q ^ n - 1) / (q - 1) % MOD    的时候注意有除法,在取余这个运算里没有 (a / b) % c = (a % c / b % c) % c,所以我们需要找个其他办法,那就是逆元

定义: a * b = 1 (mod c) 那么b就是a的逆元,在取余这个二元关系中。

性质: (a / b) % c == (a * bt) % c   其中bt是b的逆元。

这里需要求(q - 1) 的逆元, 注意到MOD是素数, 也就自然而然想到费马小定理 ====> a ^ (p - 1) = 1 (mod p) 其中p是素数

那么a的逆元at = a ^ (p - 2)  所以 (q - 1) 的逆元就是 (q - 1) ^ (MOD - 2)

剩下的就好办了。

以下代码亲测124ms,比二分慢了近一倍,因为有重复计算。。

<span style="font-size:18px;">/*************************************************************************
    > File Name: 327C.cpp
    > Author: Triose
    > Mail: Triose@163.com 
    > Created Time: 2016年08月01日 星期一 08时30分55秒
 ************************************************************************/

//#include<bits/stdc++.h>
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#include<map>
#include<set>
using namespace std;
//#define ONLINE_JUDGE
#define eps 1e-8
#define inf 0x3f3f3f3f
#define INF 0x7fffffff
#define INFL 0x3f3f3f3f3f3f3f3fLL
#define enter putchar(10)
#define rep(i,a,b) for(int i = (a); i < (b); ++i)
#define repe(i,a,b) for(int i = (a); i <= (b); ++i)
#define mem(a,b) (memset((a),b,sizeof(a)))
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define sfs(a) scanf("%s",a)
#define pf(a) printf("%d\n",a)
#define pfd(a,b) printf("%d %d\n",a,b)
#define pfP(a) printf("%d %d\n",a.fi,a.se)
#define pfs(a) printf("%s\n",a)
#define pfI(a) printf("%I64d\n",a)
#define ds(a) int a; sf(a)
#define PR(a,b) pair<a,b>
#define fi first
#define se second
#define LL long long
#define DB double
const double PI = acos(-1.0);
const double E = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
template<class T> inline T Min(T a, T b) { return a < b ? a : b; }
template<class T> inline T Max(T a, T b) { return a > b ? a : b; }
int n, m;
const LL MOD = (1e+9) + 7;
#define N 100010
char str[N];
LL len, q;
LL C;

LL PowMod(LL a, LL t, LL mod) {
    LL res = 1; 
    LL base = a % mod;
    while(t) {
	if(t & 1)
	    res = (res * base) % mod;
	base = (base * base) % mod;
	t >>= 1;
    }
    return res;
}


LL get_mod(int pos) {
    LL A = PowMod(2, pos - 1, MOD);
    LL B = PowMod(q, n, MOD);
    B = (B - 1 + MOD) % MOD;
    return (((A * B) % MOD) * C) % MOD;
}



int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//  freopen("Out.txt", "w", stdout);
#endif
    while(~sfs(str + 1)) {
	sf(n);
	len = strlen(str + 1);
	q = PowMod(2, len, MOD);
	LL ans = 0;
	C = PowMod(q - 1, MOD - 2, MOD);//逆元
	repe(i, 1, len) {
	    if(str[i] == '5' || str[i] == '0') {
		ans = (ans + get_mod(i)) % MOD;
	    }
	}
	pfI(ans);
    }
    return 0;
}</span>

现在贴改进了的:B这个数从头到尾都没变,所以一开始就计算出来就好了。。

<span style="font-size:18px;">/*************************************************************************
    > File Name: 327C.cpp
    > Author: Triose
    > Mail: Triose@163.com 
    > Created Time: 2016年08月01日 星期一 08时30分55秒
 ************************************************************************/

//#include<bits/stdc++.h>
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#include<map>
#include<set>
using namespace std;
//#define ONLINE_JUDGE
#define eps 1e-8
#define inf 0x3f3f3f3f
#define INF 0x7fffffff
#define INFL 0x3f3f3f3f3f3f3f3fLL
#define enter putchar(10)
#define rep(i,a,b) for(int i = (a); i < (b); ++i)
#define repe(i,a,b) for(int i = (a); i <= (b); ++i)
#define mem(a,b) (memset((a),b,sizeof(a)))
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define sfs(a) scanf("%s",a)
#define pf(a) printf("%d\n",a)
#define pfd(a,b) printf("%d %d\n",a,b)
#define pfP(a) printf("%d %d\n",a.fi,a.se)
#define pfs(a) printf("%s\n",a)
#define pfI(a) printf("%I64d\n",a)
#define ds(a) int a; sf(a)
#define PR(a,b) pair<a,b>
#define fi first
#define se second
#define LL long long
#define DB double
const double PI = acos(-1.0);
const double E = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
template<class T> inline T Min(T a, T b) { return a < b ? a : b; }
template<class T> inline T Max(T a, T b) { return a > b ? a : b; }
int n, m;
const LL MOD = (1e+9) + 7;
#define N 100010
char str[N];
LL len, q;
LL B, C;

LL PowMod(LL a, LL t, LL mod) {
    LL res = 1; 
    LL base = a % mod;
    while(t) {
	if(t & 1)
	    res = (res * base) % mod;
	base = (base * base) % mod;
	t >>= 1;
    }
    return res;
}


LL get_mod(int pos) {
    LL A = PowMod(2, pos - 1, MOD);
    return (((A * B) % MOD) * C) % MOD;
}



int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//  freopen("Out.txt", "w", stdout);
#endif
    while(~sfs(str + 1)) {
	sf(n);
	len = strlen(str + 1);
	q = PowMod(2, len, MOD);
	LL ans = 0;
	C = PowMod(q - 1, MOD - 2, MOD);    // q - 1 % MOD 的乘法逆元
	B = PowMod(q, n, MOD);
	B = (B - 1 + MOD) % MOD;
	repe(i, 1, len) {
	    if(str[i] == '5' || str[i] == '0') {
		ans = (ans + get_mod(i)) % MOD;
	    }
	}
	pfI(ans);
    }
    return 0;
}</span>



下面说等比数列二分求和取模:


sum(n) = q ^ 1 + q ^ 2 + ...... + q ^ n

n为偶数:

sum(n) = q ^ 1 + q ^ 2 + ... + q ^ (n / 2) + q ^ ((n / 2) + 1) + q ^ ((n / 2) + 2) + ... + q ^ (n / 2 + n / 2)

        = (1 + q ^ (n / 2)) * sum(n / 2)

n为奇数:

sum(n) = q ^ 1 + q ^ 2 + ... + q ^ ((n - 1) / 2) + q ^ ((n - 1) / 2   + 1) + ... + q ^ ((n - 1) / 2 + (n - 1) / 2) + q ^ (n)

        = (1 + q ^ ((n - 1) / 2) ) * sum((n - 1) / 2)    +    q ^ n

所以:

<span style="font-size:18px;">LL PowMod(LL A, LL T, LL mod) {		// A ^ T % mod
    LL res = 1;
    LL base = A % mod;
    while(T) {
	if(T & 1) 
	    res = (res * base) % mod;
	base = (base * base) % mod;
	T >>= 1;
    }
    return res;
}

LL PowSumMod(LL q, LL T, LL mod) {	// (q ^ 1 + .... + q ^ T) % mod
    if(T == 0) return 0;
    if(T == 1) return q % mod;
    if(T % 2 == 0) {
	return (((PowMod(q, T / 2, mod) + 1) % mod) * PowSumMod(q, T / 2, mod)) % mod;
    }
    else {
	return ((((PowMod(q, T / 2, mod) + 1) % mod) * PowSumMod(q, T / 2, mod)) % mod + 
		PowMod(q, T, mod)) % mod;
    }
}</span>

接下来贴代码:62ms

<span style="font-size:18px;">/*************************************************************************
    > File Name: 327C1.cpp
    > Author: Triose
    > Mail: Triose@163.com 
    > Created Time: 2016年08月01日 星期一 10时29分31秒
 ************************************************************************/

//#include<bits/stdc++.h>
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#include<map>
#include<set>
using namespace std;
//#define ONLINE_JUDGE
#define eps 1e-8
#define inf 0x3f3f3f3f
#define INF 0x7fffffff
#define INFL 0x3f3f3f3f3f3f3f3fLL
#define enter putchar(10)
#define rep(i,a,b) for(int i = (a); i < (b); ++i)
#define repe(i,a,b) for(int i = (a); i <= (b); ++i)
#define mem(a,b) (memset((a),b,sizeof(a)))
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define sfs(a) scanf("%s",a)
#define pf(a) printf("%d\n",a)
#define pfd(a,b) printf("%d %d\n",a,b)
#define pfP(a) printf("%d %d\n",a.fi,a.se)
#define pfs(a) printf("%s\n",a)
#define pfI(a) printf("%I64d\n",a)
#define ds(a) int a; sf(a)
#define PR(a,b) pair<a,b>
#define fi first
#define se second
#define LL long long
#define DB double
const double PI = acos(-1.0);
const double E = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
template<class T> inline T Min(T a, T b) { return a < b ? a : b; }
template<class T> inline T Max(T a, T b) { return a > b ? a : b; }
int n, m;
#define N 100010
const LL MOD = (1e+9) + 7;
char str[N];
int len;

LL PowMod(LL A, LL T, LL mod) {		// A ^ T % mod
    LL res = 1;
    LL base = A % mod;
    while(T) {
	if(T & 1) 
	    res = (res * base) % mod;
	base = (base * base) % mod;
	T >>= 1;
    }
    return res;
}

LL PowSumMod(LL q, LL T, LL mod) {	// (q ^ 1 + .... + q ^ T) % mod
    if(T == 0) return 0;
    if(T == 1) return q % mod;
    if(T % 2 == 0) {
	return (((PowMod(q, T / 2, mod) + 1) % mod) * PowSumMod(q, T / 2, mod)) % mod;
    }
    else {
	return ((((PowMod(q, T / 2, mod) + 1) % mod) * PowSumMod(q, T / 2, mod)) % mod + 
		PowMod(q, T, mod)) % mod;
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//  freopen("Out.txt", "w", stdout);
#endif
    while(~sfs(str + 1)) {
	sf(n);
	len = strlen(str + 1);
	LL q = PowMod(2, len, MOD);
	LL sum = PowSumMod(q, n - 1, MOD);
	sum = (sum + 1) % MOD;
	LL ans = 0;
	repe(i, 1, len) {
	    if(str[i] == '0' || str[i] == '5') {
		LL tmp = PowMod(2LL, i - 1, MOD);
		tmp = (tmp * sum) % MOD;
		ans = (ans + tmp) % MOD;
	    }
	}
	pfI(ans);
    }
    return 0;
}</span>





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值