[CodeForces][数学][组合][快速幂] CF327C Magic Five

题面

首先可以想到 \(5\) 的倍数一定以 \(5\)\(0\) 结尾,也就是我们选择的一段数字,只要保证结尾为 \(0\)\(5\),前面数字是什么是无关答案,可以任选的。、
那么我们就先考虑在 a 这个串中如何统计答案,最后将 \(k\) 个串平起来的答案直接乘上去就好了。

对于字符串 a,记最高位为第 \(0\) 位,最低位为第 \(n-1\) 位,根据乘法及加法原理,有:

\[ans_a = 2^0 \times (a_0 == 5 || a_0 == 0) + 2^1 \times (a_1 == 5 || a_1 == 0) + \cdots + 2^{n-1} \times (a_{n-1} == 5 || a_{n-1} == 0) \]

那么我们对于 \(k\)a,有这样的方案:

\[ans_1 = ans_a \ \times\ (2^n)^0 \\ ans_2 = ans_a \ \times\ (2^n)^1 \\ ans_3 = ans_a \ \times\ (2^n)^2 \\ \cdots \\ ans_k = ans_a \ \times\ (2^n)^{k-1} \]

对答案求一下和,发现其实就是等比数列求和,得到以下结果:

\[ANS = ans_1 \ \times\ \frac{2^{nk}-1}{2^n-1} \]

其中 \(n\) 为字符串长度。
于是这道题只剩下轻松愉快的快速幂和费马小定理求逆元了。

代码:

// 只要保证最后一位是 0 或 1,就能被 5 整除
// 所以答案是每一个 5 或非前导 0 之前的部分求方案
// s 不需要显式的建出来,每一个串对应多更新答案
// 前导 0 计算在结果中

# include <iostream>
# include <cstdio>
# include <string>

const long long MOD = 1e9+7;

long long QPow(long long x, long long y){
	long long ans = 1, base = x%MOD;
	while(y){
		if(y & 1){
			ans = (ans * base) % MOD;
		}
		base = (base * base) % MOD;
		y >>= 1;
	}
	return ans;
}

long long Inv(long long x){
	return QPow(x, MOD-2);
}

int main(){
	std::string a; int k;

	std::cin>>a>>k;

	long long ans = 0;
	for(int i = 0; i <= a.length()-1; i++){
		if(a[i] == '0' || a[i] == '5'){
			ans = (ans + QPow(2, i)) % MOD;
		}
	}

	printf("%lld", (((ans * (QPow(2, 1ll*a.length()*k) - 1)) % MOD) * Inv(QPow(2, a.length())-1)) % MOD);

	return 0;
}
posted @ 2020-09-24 20:29  ChPu437  阅读(124)  评论(0编辑  收藏  举报