AtCoder Beginner Contest 227 G - Divisors of Binomial Coefficient (埃筛分解较大数)

G - Divisors of Binomial Coefficient

Link
在这里插入图片描述
题目大意:计算组合数 C n k C^k_n Cnk 的约数个数。 1 ≤ k ≤ 1 e 6 , 1 ≤ n ≤ 1 e 12 1≤k≤1e6,1≤n≤1e12 1k1e61n1e12,且保证 k ≤ n k≤n kn

首先可见约数个数定理

然后我们有: C n k = n × ( n − 1 ) × ( n − 2 ) × . . . ( n − k + 1 ) 1 × 2 × 3 × . . . . × k C_n^k=\frac{n×(n-1)×(n-2)×...(n-k+1)}{1×2×3×....×k} Cnk=1×2×3×....×kn×(n1)×(n2)×...(nk+1)

所以我们只要分别对上下素因子分解即可求得答案。分母可以直接分解,但主要是分子的 n n n 比较大,我们不能通过处理出最小素因子快速分解。但是我们发现,分子的数的种类数最多也就 1 e 6 1e6 1e6 个,且 n ≤ 1 e 6 \sqrt n≤1e6 n 1e6 ,那么我们可以通过类似埃筛的方式对这些数进行分解。

我们首先可以处理出 m a x ( k , n ) max(k,\sqrt n) max(k,n ) 内的所有素数。然后我们对于每个素数,去筛其在 [ n − k + 1 , n ] [n-k+1,n] [nk+1,n] 内的所有倍数,在筛的时候不断的除完这个素因子,这里用代码更好看懂。

int offset = n - k; //偏移量
for (ll i = 1; i <= k; ++i) {
	up[i] = i + offset;
}
for (int idx = 0; idx < cnt; ++idx) {
	int p = prime[idx], tot = 0;
	for (ll i = (n - k + p) / p * p; i <= n; i += p) {
		while(up[i - offset] % p == 0) {
			tot++;
			up[i - offset] /= p;
		}
	}
	ans = ans * (tot + 1) % mod;
}

那么做完上述过程后,也就是除完之后还不为 1 1 1 的话,也就是说有大于根号的素因子,那么其必只有一个,那么可以直接统计了。

c o d e code code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e6 + 100;
const ll mod = 998244353;

int vis[N], prime[N], cnt;

ll n, k, offset, ans = 1, limit, up[N], lo[N];

void init() {
	offset = n - k;
	limit = max(k, (ll)sqrt(n + 0.5));
	for (ll i = 1; i <= k; ++i) {
		lo[i] = i;
		up[i] = i + offset;
	}
	for (int i = 2; i <= limit; ++i) {
		if (!vis[i]) prime[cnt++] = i;
		for (int j = 0; j < cnt && 1ll * i * prime[j] <= limit; ++j) {
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%lld%lld", &n, &k);
	init();
	for (int idx = 0; idx < cnt; ++idx) {
		int p = prime[idx], tot = 0;
		for (ll i = p; i <= k; i += p) {
			while(lo[i] % p == 0) {
				tot--;
				lo[i] /= p;
			}
		}
		for (ll i = (n - k + p) / p * p; i <= n; i += p) {
			while(up[i - offset] % p == 0) {
				tot++;
				up[i - offset] /= p;
			}
		}
		ans = ans * (tot + 1) % mod;
	}
	for (int i = 1; i <= k; ++i) {
		if (up[i] ^ 1) {
			ans = (ans + ans) % mod;
		}
	}
	printf("%lld", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值