poj2992
题意:给n,k,求c(n,k)的约束个数。
分析:用组合数学的方法求因子个数,我们分别对n!,(n-k)!,k!分解质因数。c(n,k)=n!/((n-k)!k!),把对应因子个数相减,我们就得到了c(n,k)分解的结果。把取每个质因子的方法数相乘即为所求。所以我们需要一种快速分解x!的方法。我们可以枚举所有x!中存在的素因子,对于每个素因子求个数的方式来分解。而对于一个素因子p,我们求在x!中的方法如下,1~x中有x/p个数可以被p整除,所以x!中至少有x/p个p。然后我们再看有多少个p^2。能被p^2整除的数一定能被p整除。所以我们先将1~x中不能被p整除的数刨除。再将剩下的数除以p。现在还能被p整除的数,原来一定包含p^2。而现在剩下的数是1~x/p,问题被我们转化为了一个子问题。(x/p)!中包含多少p。这样递归求解即可。
View Code
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define maxn 505
int n, k;
bool is[maxn];
int pos[maxn];
int prm[maxn];
int f[maxn], g[maxn];
int getprm(int n)
{
int i, j, k = 0;
int s, e = (int) (sqrt(0.0 + n) + 1);
memset(is, 1, sizeof(is));
prm[k++] = 2;
is[0] = is[1] = 0;
for (i = 4; i < n; i += 2)
is[i] = 0;
for (i = 3; i < e; i += 2)
if (is[i])
{
pos[i] = k;
prm[k++] = i;
for (s = i * 2, j = i * i; j < n; j += s)
is[j] = 0;
}
for (; i < n; i += 2)
if (is[i])
{
pos[i] = k;
prm[k++] = i;
}
return k;
}
int cal(int n, int &p)
{
if (n < p)
return 0;
return n / p + cal(n / p, p);
}
int main()
{
//freopen("t.txt", "r", stdin);
int t = getprm(500);
while (scanf("%d%d", &n, &k) != EOF)
{
long long ans = 1;
for (int i = 0; i < t && prm[i] <= n; i++)
ans *= cal(n, prm[i]) - cal(n - k, prm[i]) - cal(k, prm[i]) + 1;
printf("%lld\n", ans);
}
return 0;
}