Codeforces 493 E.Devu and Birthday Celebration

## [$>Codeforces \space 493\ E.Devu\ and\ Birthday\ Celebration<$](http://codeforces.com/problemset/problem/493/E)

题目大意 : 有 \(q\) 组询问,每次有 \(n\) 小朋友和 \(m\) 个苹果,求有多少种方案,使得每个小朋友都分到一个苹果,且每个小朋友分到的苹果的数目的 \(gcd = 1\),输出答案对 \(10^9 + 7\) 取模的值

\(1 \leq n, m, q \leq 10^5\)

解题思路 :

\(f(n)\) 表示分苹果的方案满足 \(n | gcd\) 的方案数,\(g(n)\) 表示分苹果的方案满足 \(gcd = n\) 的方案数

显然有 \(f(n) = \sum_{n|d} g(d)\)

考虑到 \(f(n)\) 本质上是将 \(\frac{M}{n}\) 个球,放到 \(N\) 个不同盒子里盒子不能为空的方案数,插板法组合数求解即可

通过简单的莫比乌斯反演可以得到 \(g(n) = \sum_{n|d} \mu(\frac{n}{d}) f(d)\)

实际上对于每一组询问我们要求的就是 \(g(1)\) 的值,而对于 \(f(n)\) 来说,只有当 \(n|M\) 的时候才可能有值

所以只需要大力枚举 \(M\) 的因子,预处理组合数和莫比乌斯函数 \(O(1)\) 计算单个 \(f\) 的值即可

总复杂度是 \(O(Q\sqrt{m})\)


/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define int ll

const int N = 200005, Mod = 1000000007;
int js[N], mu[N], b[N], prime[N], inv[N], tot, n, m;

inline int Pow(int a, int b){
    int ans = 1;
    for(; b; b >>= 1, a = a * a % Mod) 
        if(b & 1) ans = ans * a % Mod; 
    return ans;
}
inline void Prework(){
    mu[1] = 1, js[0] = 1, inv[0] = 1;
    for(int i = 1; i < N; i++) 
        js[i] = js[i-1] * i % Mod, inv[i] = Pow(js[i], Mod - 2);
    for(int i = 2; i < N; i++){
        if(!b[i]) prime[++tot] = i, mu[i] = -1;
        for(int j = 1; j <= tot && i * prime[j] < N; j++){
            b[i*prime[j]] = 1;
            if(i % prime[j]) mu[i*prime[j]] = -mu[i]; else break; 
        } 
    }
}

inline int C(int n, int i){ return js[n] * inv[i] % Mod * inv[n-i] % Mod; }
inline int calc(int x){ return x < m ? 0 : C(x - 1, m - 1) * mu[n/x] % Mod; }

inline void solve(){
    int ans = 0; read(n), read(m);
    int num = (int) sqrt(n);
    for(int i = 1; i <= num; i++) if(n % i == 0){
        int x = i, y = n / i;
        (ans += calc(x)) %= Mod;
        if(x != y) (ans += calc(y)) %= Mod; 
    }
    printf("%lld\n", ((ans % Mod) + Mod) % Mod);
}

signed main(){
    Prework();
    int T; read(T); while(T--) solve();
    return 0;
}
posted @ 2018-08-08 21:12  Joyemang33  阅读(131)  评论(0编辑  收藏  举报