寒假集训专题四-C.beautiful number


beautiful numbers

题意

输入a,b,n,由a和b组成的长n的数字,求各位数之和仍为a和b组成的数字的个数

思路

对n进行判断,发现就是求成功a,b个数的组合数之和。

通过对a个数的枚举来判断符合条件的a的个数,然后把组合数加入最后的结果

这里用到一个技巧-----组合数的计算以及乘法逆元

知识点:逆元

这题可以直接用费马小定理或者扩欧求,但若遇到数据量大的,会超时,如牛客寒假集训第6场牛妹的数学难题

利用费马小定理:

\[a^{p-1}≡1{(mod \ p)}\\ a*x≡1{(mod \ p)} \]

可得逆元为

\[x=a^{p-2} \]

线性求逆元方法

1.使用公式递推
这个公式的来源:
因为11^(-1)≡1
可得 i * i^(-1)恒等1,即求 i^-1
令k=p/i p=k * i + j 得 两边mod p 有 k
i+j≡0(mod p)
两边除以 j * i 得:k * j^(-1) + i^(-1) ≡ 0 (mod p)
有 i^-1 ≡ -(p / i) * (p mod i) ^ (-1) (mod p)
可以使用递推实现,或者递归+记忆化
最后-p/i转化为 (p-p/i) 因为是负的 在c++中mod是按负的来所以要转化为floor取模 即可以加上要取模
(转化为正数)的数再取模

	f[0]=f[1]=1;
    inv[0]=inv[1]=1;
	for(int i=2;i<=N-1;i++)
	{
		f[i]=i;
		f[i]=f[i-1]*f[i]%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}

2.前缀积求总逆元,再往回推

	fv[N-1]=qpow(f[N-1],mod-2);
	for(int i=N-1;i>=1;i--)
	{
		fv[i-1]=fv[i]*i%mod;
	}

代码

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
using ll = long long;
const ll N=1e6+1;
const ll mod = 1e9+7;
ll a, b;
ll f[N];
//快速幂
ll qpow(ll x, ll n)
{
	ll base=1;
	while(n)
	{
		if(n&1)
		{
			base=base*x%mod;
		}
		x=x*x % mod;
		n=n>>1;
	}
	return base;
}
//记忆化阶乘,注意0的阶乘为1
void jiecheng()
{
	f[0]=1;
	f[1]=1;
	rep(i,2,N-1)
	{
		f[i]=i;
		f[i]=f[i]*f[i-1] % mod;
	}
}
//组合数的写法
ll c(ll m,ll n)
{
	return f[n]*qpow(f[m],mod-2)%mod*qpow(f[n-m],mod-2)%mod;
}
bool check(ll x)
{
	ll t;
	while(x)
	{
		t=x % 10;
		if(t != a && t != b)
		{
			return false;	
		}
		x=x/10;	
	} 
	return true;
}

signed main()
{
	jiecheng();
	
	ll ans = 0;
	ll n;
	ll temp;
	cin>>a>>b>>n;
	rep(i,0,n)
	{
		temp=a*i+b*(n-i);
		if(check(temp))
		{
			ans =(ans+c(i,n))% mod;
		}
	}
	cout<