Cba=a×(a−1)×…×(a−b+1)b×(b−1)×…×1=a×(a−1)×…×(a−b+1)×(a−b)!b×(b−1)×…×1×(a−b)!=a!b!(a−b)!(分解质因数)=pα11pα22…pαkk求分子分解每个p的次数cntmi(a!)=[ap]下取整+[ap2]下取整+[ap3]下取整+…=p的倍数中∈[1,a]的个数+p2的倍数中∈[1,a]的个数+…为什么只+1而不是+k,因为a中pk低阶项的次数已经被算过了比如p3既是p的倍数也是p2的倍数比如8!=1×2×3×4×5×6×7×8求其中2的个数,则1~8之间含2的一次有2、4、6、8四个2,含2二次方有4、8,但4、8中2的一次方已经记过一次了,所以只加二次方中的两个2含2三次方有8,但8中的一次、二次方都被记过一次了,所以只加三次方中的一个2同理可类推到pk它的幂次中的前k−1个p在计算p1∼pk−1次数中都已经算过1次了同理可求分母的p的次数,答案=∏ip分子次数和−分母次数和i
具体:
- 筛素数(1~5000)
- 求每个质数的次数
- 用高精度乘把所有质因子乘上
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=5010;
int primes[N],cnt;
int sum[N];
bool st[N];
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])primes[cnt++]=i;
for(int j=0;primes[j]*i<=n;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0)break;//==0每次漏
}
}
}
// 对p的各个<=a的次数算整除下取整倍数
int get(int n,int p)
{
int res =0;
while(n)
{
res+=n/p;
n/=p;
}
return res;
}
//高精度乘
vector<int> mul(vector<int> a, int b)
{
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t)
{
c.push_back(t % 10);
t /= 10;
}
// while(C.size()>1 && C.back()==0) C.pop_back();//考虑b==0时才有pop多余的0 b!=0不需要这行
return c;
}
int main()
{
int a,b;
cin >> a >> b;
get_primes(a);
for(int i=0;i<cnt;i++)
{
int p = primes[i];
sum[i] = get(a,p)-get(a-b,p)-get(b,p);//是a-b不是b-a
}
vector<int> res;
res.push_back(1);
for (int i = 0; i < cnt; i ++ )
for (int j = 0; j < sum[i]; j ++ )//primes[i]的次数
res = mul(res, primes[i]);
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
puts("");
return 0;
}
这段代码是用来计算组合数 Cba 的值的。
首先,通过线性筛法求出 1 到 a 之间的所有质数,并记录下来。
然后,对于每个质数 p,通过函数
get
计算出 a! 中 p 的次数、(a−b)! 中 p 的次数、b! 中 p 的次数,然后相减得到 p 的次数,存储在数组sum
中。最后,将所有质数的次数相乘,得到组合数的值。
其中,函数
mul
是用来实现高精度乘法的。次数相减可能会出现负数吗?
不会
因为C(a,b)为一个整数,也就是对于每个质数的次数,分子的次数一定是大于等于分母的(不然就不能整除),所以sum[i]>=0
for (int i = 0; i < cnt; i )
for (int j = 0; j < sum[i]; j )//primes[i]的次数
res = mul(res, primes[i]);
这一步是为什么呀,不太理解嘞,求佬解读
primes[i]的次数是sum[i],因此res需要累乘sum[i]个primes[i],j用来计算累乘primes[i]的次数
大佬厉害
精辟
%%%
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
这个地方能用push_back吗,这样子不是每算一次都会逆序一下吗
c数组和a数组不是本来就倒序存放么,最后我们输出的时候倒序输出就正序了
你好,我想问一下,下取整的符号怎么打出来的
平时的除号就是下取整,
⌊ap⌋ \lfloor \dfrac{a}{p} \rfloor
高精度乘法这样写为啥过不了这个题
vector<int> mul(vector<int>& a, int b){ vector<int> c; int t = 0; for(int i = 0; i < a.size() || t; i ++ ){ t += a[i] * b; c.push_back(t % 10); t /= 10; } return c; }
但i == a.size()时,t不为0继续执行,但此时a[i]越界了。。
纯个人看法,不知道C++vector越界是怎么处理的,可能不是越界的问题
楼主参考一下即可
你的for执行完,t里可能还有值呀
多加一行就好了:while (t) C.push_back(t % 10), t /= 10;
t里面是有值,此时不应该继续执行for循环吗?
噢没仔细看你的for循环,这样的话,那就是t有值时继续执行后a数组越界了
是的,可能是楼上老哥说的那样,但是vector越界不会扩容吗?然后值为0?
嗯是滴,不会扩容,访问不到
是的,还是这样写保险
for(int i = 0; i < a.size() || t; i ++ ){ if(i < a.size())t += a[i] * b; c.push_back(t % 10); t /= 10; }
y总的 “793. 高精度乘法” 数据不强,我这样写数据全过了
越界了访问不了,但不会报错,是越界的问题
此楼正解,赞。
羡慕交大王者
%%%
女少口阿
比如说在1,2,3,4,5,6,7,8里面找2的个数,那我肯定先找2,不就是2,4,6,8四个吗,那下一步我就是找2的倍数,也就4,然后4,8两个,然后找8,8就一个,这样也就是2的个数也就是7次,然后8/2+8/4+8/8=7,,8/2得4,因为1~8里面偶数的差2就4个,2,4,6,8,1~8里面偶数差4的就两个,之后同理
id有点秀
nice,赞
含2,4,6,8一共4个2的一次
想问一下为啥sum[i]>=0
可以反证法想一下
如果sum[i]<0则说明什么,说明Cba拆分为素数乘积的话会出现一个素数的倒数,但a>=b时都是正整数,也就不可能拆为不同素数的乘积时出现有素数倒数的情况
我是这样想的:sum[i]表示的是Cba中包含的第i个质数p的次数 一个数的要么存在值为p的质因子要么不存在值为p的质因子 故sum[i]>=0 不可能出现sum[i]<0的情况
for (int i = 0; i < cnt; i ++ ) for (int j = 0; j < sum[i]; j ++ )//primes[i]的次数 res = mul(res, primes[i]);
这个为什么不能这么写
for (int i = 0; i < cnt; i ++ ) if(primes[i]*sum[i]) res = mul(res, primes[i]*sum[i]);
是要乘primes[j]^sum[i],你这么写是乘primes[j]*sum[i]了
这句加一说到点子上了 手动点赞