2023牛客暑期多校训练营6-C-idol!!

 奇数的双阶乘等于小于等于本身的奇数的乘积,偶数的双阶乘等于小于等于本身的非零偶数的乘积。

思路:考虑末位0的个数,我们能想到的最小两数相乘有零的就是2*5,所以本题我们思路就是去找因子2的个数以及因子5的个数,2的个数肯定比5的个数,所以我们只需要去找因子5的个数就能知道末位有几个零。这里给个例子:2*15的结果是有一个零,是因为只有一个2和5的因子;4*25的结果末位有两个零,是因为各有两个2和5的因子。

下面主要考虑的就是如何去找5的因子,其实很简单,一个数能被5整除就有1个5的因子,若能被25整除就有两个5的因子,能被5^{k}整除就有k个5的因子。因为求的是双阶乘的积,举个例子对于5来说,大于等于5的奇数的阶乘都包含5,能求出5~n的奇数个数=(n-5)/2+1,就是5对整个阶乘积贡献的5的个数。由于5^{k}的倍数包含奇偶,所以要分为奇数偶数讨论,对于所有奇数5^{k},3*5^{k},5*5^{k}......对总的贡献5的个数都等于本身到n的奇数的个数=(n-m*5^{k})/2+1

不难发现是个等差数列,这里能求出5^{k}倍数的项数=n/(5^{k}),根据项数奇偶性去得出等差数列项数,还能求出等差数列末项,再根据等差数列求和公式就能得出结果。偶数也类似,首项尾项以及项数都能根据奇数情况轻松得到。

要爆longlong!!!!int_128好用

#include
using namespace std;
#define ll long long
const ll mod=998244353;
inline void printint(__int128 x)
{
    if(x>=10) printint(x/10);
    putchar('0'^(x%10));
}
__int128 a[35];//5的幂 
 
signed main(){
	int k=0;
	a[0]=1;
	while(a[k]*5<=1e18)
	{
		k++;
		a[k]=a[k-1]*5;
	}
	ll _n;
	cin>>_n;
	__int128 n=(__int128)_n;
	__int128 ans=0;
	for(int i=1;i<=k&&a[i]<=n;i++)
	{
		__int128 num=n/a[i];
		__int128 a1=(n-a[i])/2+1;//首项 
		__int128 h=a[i]*num;//尾项的值 
		if(num&&num%2!=0)
		{
			//先求奇数倍数等差数列 
			__int128 an=(n-h)/2+1;//尾项 
			ans+=(num/2+1)*(a1+an)/2;//公式求和 
			//偶数倍数 首项尾项变 
			an=(n-h+a[i])/2+1;
			a1=(n-a[i]*2)/2+1; 
			ans+=(num/2)*(a1+an)/2;
		}
		else if(num&&num%2==0) 
		{
			//先是奇数倍数等差数列 
			num/=2;
			a1=(n-a[i])/2+1;//首项 
			ll an=a[i]*num*2-a[i];
			an=(n-an)/2+1;//尾项 
		 	ans+=num*(a1+an)/2;
		 	//偶数 
		 	a1=(n-a[i]*2)/2+1;
		 	an=(n-a[i]*num*2)/2+1;
			ans+=(num)*(a1+an)/2; 
		}
	//	cout<

你可能感兴趣的:(牛客2023暑假多校,数论,算法,数据结构)