[置顶]LCM性质 + 组合数 - HDU 5407 CRB and Candies

2024-09-05 16:32

本文主要是介绍[置顶]LCM性质 + 组合数 - HDU 5407 CRB and Candies,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 CRB and Candies

Problem's Link


 Mean: 

给定一个数n,求LCM(C(n,0),C(n,1),C(n,2)...C(n,n))的值,(n<=1e6).

analyse:

很有趣的一道数论题!

看了下网上别人的做法,什么Kummer定理我还真没听说过,仔细研究一下那个鬼定理真是涨姿势了!

然而这题我并不是用Kummer那货搞的(what?).

其实这题真的很简单(不要打我),为什么这样说呢?看了下面的解释你就知道我没骗你。

首先我们看一下这个式子:LCM(C(n,0),C(n,1),C(n,2)...C(n,n))

当时我的第一感觉是:晕,还是打个表吧!结果,打表程序后台打了四个半小时也没打完=.=(时间复杂度算错了)

做这题首先你得知道这个(基本常识):

求多个数的最小公倍数,有两种方法:

1)分解质因数法:

先把这几个数分解质因数,再把它们一切公有的质因数和其中几个数公有的质因数以及每个数的独有的质因数全部连乘起来,所得的积就是它们的最小公倍数。

例如,求LCM[12,18,20,60]

因为12=(2)×[2]×[3],18=(2)×[3]×3,20=(2)×[2]×{5},60=(2)×[2]×[3]×{5}

其中四个数的公有的质因数为2(小括号中的数),

三个数的公有的质因数为2与3[中括号中的数],

两个数的公有的质因数为5{大括号中的数},

每个数独有的质因数为3。

所以,[12,18,20,60]=2×2×3×3×5=180。

2)公式法:

由于两个数的乘积等于这两个数的最大公约数与最小公倍数的积。

即(a,b)×[a,b]=a×b。

所以,求两个数的最小公倍数,就可以先求出它们的最大公约数,然后用上述公式求出它们的最小公倍数。

例如,求[18,20]

即得[18,20]=18×20÷(18,20)=18×20÷2=180。

求几个自然数的最小公倍数,可以先求出其中两个数的最小公倍数,

再求这个最小公倍数与第三个数的最小公倍数,依次求下去,直到最后一个为止。

最后所得的那个最小公倍数,就是所求的几个数的最小公倍数。

知道这个后,做这题选择哪种方法呢?

如果选择第二种方法,恭喜你,你绝壁和我一样想到打表滚粗!

既然第二种方法不行,肯定只能是第一种方法了。

那么要怎么做呢?

首先我们来看,对于组合数C(n,m),可以有如下变换:

C(n,m)=n!/[(n-m)!*m!]=n*(n-1)*(n-2)*....(m+1) / (n-m)! 

这一步应该没问题吧!

也就是:C(n,m)=n!/[(n-m)!*m!]=n*(n-1)*(n-2)*....(m+1) / (n-m)!  = n*(n-1)*(n-2)*....(m+1)/1/2/3/4/5/..../(n-m)

我们把前后结合一下,边乘边除:

对于第k步,就相当于*(n+1-k)且/k,k={1,2,...n-m}.

我们以n=8为例:

C(8,0)=1

C(8,1)=8*7*6*5*4*3*2 /7/6/5/4/3/2/1

C(8,2)=8*7*6*5*4*3 /6/5/4/3/2/1

C(8,3)=8*7*6*5*4 /5/4/3/2/1

C(8,4)=8*7*6*5 /4/3/2/1

C(8,5)=8*7*6 /3/2/1

C(8,6)=8*7 /2/1

C(8,7)=8 /1

C(8,8)=1

结合求n个数的LCM的方法,我们将问题转换成:

找i个数共有的质数,然后相乘就可,i={1,2,..n}。

好了,你可能会说:*$#@*@,找i个数共有的质数难道不超时,而且你的代码里连一个0~n的for循环都没有,你在逗我?

不急,看下面:

首先我们明确一点,C(n,k)的最大质因数是不会大于n的。

那么对于一个质数p来说,他对"n个数的LCM"的贡献在哪?

是不是就是p^1,p^2,p^3...中的一些?

哪些呢?

前面求组合数中,我们把C(n,m)分成了分子和分母来看。

如果p^x能够整除(n-1+k),那么他有可能是满足的,但是还不够,还要看是不是会被分母抵消掉。

只有p^x满足(n-1+k)%(p^x)==0且满足k%(p^x)!=0,这个p^x才是满足的,也就是对答案才有贡献,此时ans需要乘以p。

最后一步,约约分可能会更方便:把分子分母合一下,变成了:(n-1)%(p^x)!=0,表示(n-1+k)%(p^x)==0和k%(p^x)!=0不是同时出现的,此时才满足。

OK,推导完毕。

最终方法就是:

先筛出1e6以内的所有素数p,然后判断(n-1)%(p^x)是否!=0,是的话,ans*=p。

没骗你吧,是不是很简单? ==||

Time complexity: O(p_num*sqrt(n))

Source code: 

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-08-21-15.17
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define mod 1000000007
#define  LL long long
#define  ULL unsigned long long
using namespace std;
const int NN=1000010;
bool v[NN];
int p[NN],num;
void makePrime(){
int i,j;
num=-1;
for(i=2; i<NN; ++i){
if(!v[i]) p[++num]=i;
for(j=0; j<=num && i*p[j]<NN; ++j){
v[i*p[j]]=true;
if(i%p[j]==0) break;
}
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
makePrime();
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
LL ans=1;
for(int i=0; i<=num; ++i){
for(LL t=p[i]; t<=n; t*=p[i]){
if((n+1)%t!=0)
ans=ans*p[i]%mod;
}
}
printf("%lld\n",ans);
}
return 0;
}

 

这篇关于[置顶]LCM性质 + 组合数 - HDU 5407 CRB and Candies的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139479

相关文章

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

hdu 3790 (单源最短路dijkstra)

题意: 每条边都有长度d 和花费p,给你起点s 终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。 解析: 考察对dijkstra的理解。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstrin

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri