Ural 1091. Tmutarakan Exams (莫比乌斯反演)

题目链接: http://acm.timus.ru/problem.aspx?space=1&num=1091

题意:

从1~S个数字里选出K个数使得K个数的gcd > 1的选择情况数有多少种,注意的是,如果答案大于10000,输出10000即可。K<=S<=50

思路:

很简单的莫比乌斯反演水题,设F(x)为选出k个数的gcd为x的倍数的情况数,则反演函数f(x)即为选出k个数的gcd为x的情况数就可以了。

这题数据范围小,所以其实直接用容斥原理也可以做,莫比乌斯反演其实就是计算各个容斥系数。

#include 
typedef long long ll;

const int maxn = 50 + 10;

int mu[maxn], pnum, pri[maxn];
bool vis[maxn];
ll C[maxn][maxn];

// 莫比乌斯反演函数
void Mobius(int n) {
    mu[1] = 1; pnum = 0; vis[1] = 1;
    for(int i = 2;i <= n; i++) {
        if(!vis[i]) {
            pri[pnum++] = i;
            mu[i] = -1;
        }
        for(int j = 0;j < pnum; j++) {
            if(i * pri[j] > n)  break;
            vis[i * pri[j]] = 1;
            if(i % pri[j] == 0) {
                mu[i * pri[j]] = 0;
                break;
            }
            mu[i * pri[j]] = -mu[i];
        }
    }
}

void init(int n) {
    Mobius(n);
    C[0][0] = 1;
    for(int i = 1;i <= n; i++){
        C[i][0] = 1;
        for(int j = 1;j <= n; j++)
            C[i][j] = C[i-1][j] + C[i-1][j-1];
    }
}

int main() {
    init(50);

    int k, s;
    scanf("%d%d", &k, &s);
    ll ans = 0;
    for(int i = 1;i <= s; i++) {
        int cnt = s/i;
        ans += C[cnt][k]*mu[i];
    }
    ans = C[s][k] - ans; // 计算出gcd=1的情况数,总情况减去gcd=1的情况数就是答案
    if(ans >= 10000)    ans = 10000;
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(ACM_Ural,ACM_数论and数学类)