HDU 5407 CRB and Candies (2015年多校比赛第10场)

1.题目描述:点击打开链接

2.解题思路:本题要求LCM(C(n,0), C(n,1),..., C(n,n)),官方题解是转化为求解LCM(1,2,3,...n+1)/(n+1),然而这种做法还是觉得太陌生,不妨试着用学过的唯一分解定理去做。


首先,求这n+1个数的LCM,实际上就是求所有小于n的素数中,对于每一个素数Pi,哪一项的指数最大,然后把他们连乘起来即可得到LCM值。因此,问题转化为确定每一个pi的最大指数。这里要用到Kummer定理来解决,Kummer定理告诉我们这样一个事实:p恰好整除C(n,k)的指数等于n和k都写成p进制数时候,ni<ki的个数。这里的ni指的是n写成p进制数时候的第i项。实际上等价于n-k和k这两个数按照p进制数相加时,发生的“进位”的次数。由于我们要找最大的指数,即我们希望进位的次数可以尽可能的多。在草稿纸上举几个例子演算后即可发现,如果在第i位第一次发生了进位现象,那么进位后剩下的数一定小于p-1,因为在p进制中,两个加数最大只能是p-1,然而如果想要发生进位后剩下的数也等于p-1,即相加后的结果应该是p+(p-1),这显然是不可能的。这样,只要找到了这个数,那么可以构造一个k,使得后面所有位都发生进位。由此,我们就找到了最大指数。


找到指数之后,利用快速幂计算乘积即可得到答案。

3.代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
using namespace std;

#define me(s) memset(s,0,sizeof(s))
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <ll, int> P;


const int MOD = 1e9 + 7;
const int N = 1000000 + 10;

int vis[N];
int primes[N];
int idx;
void seive()
{
	int m = sqrt(N);
	for (int i = 2; i <= m; i++)
	if (!vis[i])
	for (int j = i*i; j<N; j += i)
		vis[j] = 1;
	for (int i = 2; i<N; i++)
	if (!vis[i])
		primes[idx++] = i;
}

ll pow_mod(ll a, int k)
{
	ll res = 1;
	while (k>0)
	{
		if (k & 1)res = res*a%MOD;
		a = a*a%MOD;
		k >>= 1;
	}
	return res;
}

int get_pow(int n, int p)
{
	int cnt = 0;
	int tmp[30];
	while (n>0)
	{
		tmp[cnt++] = n%p;
		n /= p;
	}
	int res = 0, flag = 0;
	for (int i = 0; i<cnt - 1; i++)
	if (tmp[i]<p - 1 + flag)//找到第一个小于p-1的数
	{
		res++;
		flag = 1;
	}
	return res;
}

int main()
{
	seive();
	int T;
	for (scanf("%d", &T); T--;)
	{
		int n;
		scanf("%d", &n);
		ll ans = 1;
		for (int i = 0; i<idx&&primes[i] <= n; i++)
		{
			ans = ans*pow_mod(primes[i], get_pow(n, primes[i])) % MOD;
		}
		printf("%I64d\n", ans);
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值