题目链接:Codeforces 439E Devu and Birthday Celebration
题目大意:给出q,表示询问的次数,每次询问有n和f,问有多少种分类方法,将n分成f份,并且这f份的最大共约数为1.
解题思路:如果不考虑说最大共约数为1的话,那么问题很简单,就是f个数的和为n的种数C(f?1n?1).所以我们就尽量将问题转化成说f数的和为s的子问题。用容斥原理,总的可能减去公约数不为1的情况,那么公约数不为1的情况就去枚举公约数。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5+5;
const int MOD = 1e9+7;
vector<int> vec;
int fact[N], ive_fact[N];
int power (int x, int n) {
int ans = 1;
while (n) {
if (n&1)
ans = (1ll * ans * x) % MOD;
n /= 2;
x = (1ll * x * x) % MOD;
}
return ans;
}
void init () {
fact[0] = 1;
for (int i = 1; i < N; i++)
fact[i] = (1ll * fact[i-1] * i) % MOD;
for (int i = 0; i < N; i++)
ive_fact[i] = power(fact[i], MOD-2);
}
void divFactor(int cur) {
vec.clear();
int tmp = sqrt(cur+0.5);
for (int i = 2; i <= tmp; i++) {
if (cur % i == 0) {
vec.push_back(i);
while (cur % i == 0)
cur /= i;
}
}
if (cur != 1)
vec.push_back(cur);
}
int solve (int n,int f) {
if (n < f)
return 0;
int ans = fact[n];
ans = 1ll * ans * ive_fact[f] % MOD;
ans = 1ll * ans * ive_fact[n-f] % MOD;
return ans;
}
int cal (int n) {
return (n % MOD + MOD) % MOD;
}
int main () {
init();
int cas;
scanf("%d", &cas);
while (cas--) {
int n, f;
scanf("%d%d", &n, &f);
divFactor(n);
int ans = 0, t = 1<<vec.size();
for (int i = 0; i < t; i++) {
int tmp = 1, sign = 1;
for (int j = 0; j < vec.size(); j++) {
if (i&(1<<j)) {
sign *= -1;
tmp *= vec[j];
}
}
ans = cal(ans + sign * solve(n/tmp - 1, f - 1));
}
printf("%d\n", ans);
}
return 0;
}
Codeforces 439E Devu and Birthday Celebration(计数问题)
时间: 2024-10-15 05:09:04