URAL 1091 Tmutarakan Exams (DP或容斥)

来源:互联网 发布:淘宝卖家怎么处理差评 编辑:程序博客网 时间:2024/06/05 09:56

题意

给出一个K和S,求从小于S的数里取出一个K元组的gcd大于1的K元组的数量。

思路

可以dp做,dp的话是经典的计数问题,而且因为数据比较小写起来也比较简单,dp[i][j][k]表示枚举到i时取了j个数此时的gcd为k的个数,则dp[i+1][j+1][gcd(k,i+1)]+=dp[i][j][k],dp[i+1][j][k]+=dp[i][j][k]。
或者容斥,因为所有合数都能用素数来表示,所以我们先枚举所有素数,然后对各个相同的或者不同的素数和进行容斥,总之就是枚举出来所有素数的组合然后奇数加偶数减就可以了。

DP代码

#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <stack>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>#include <time.h>using namespace std;#define LL long long#define Lowbit(x) ((x)&(-x))#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1|1#define MP(a, b) make_pair(a, b)const int INF = 0x3f3f3f3f;const int maxn = 1e5 + 7;const double eps = 1e-8;const double PI = acos(-1.0);LL dp[55][55][55];int main(){    //freopen("in.txt","r",stdin);    //freopen("out.txt","w",stdout);    int K, S;    while (scanf("%d%d", &K, &S) != EOF)    {        memset(dp, 0, sizeof(dp));        for (int i = 0; i <= S; i++) dp[i][1][i] = 1;        for (int i = 1; i < S; i++)            for (int j = 1; j <= K; j++)                for (int k = 1; k <= S; k++)                {                    dp[i+1][j][k] += dp[i][j][k];                    dp[i+1][j+1][__gcd(k, i+1)] += dp[i][j][k];                }        int ans = 0;        for (int i = 2; i <= S && ans <= 10000; i++)            ans += dp[S][K][i];        if (ans > 10000) ans = 10000;        printf("%d\n", ans);    }    return 0;}

容斥代码

#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <stack>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>#include <time.h>using namespace std;#define LL long long#define Lowbit(x) ((x)&(-x))#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1|1#define MP(a, b) make_pair(a, b)const int INF = 0x3f3f3f3f;const int maxn = 1e5 + 7;const double eps = 1e-8;const double PI = acos(-1.0);vector<int> prime;vector<int> cnt;  //某个素数的可取倍数的数量int vis[55];void init(){    memset(vis, 0, sizeof(vis));    for (int i = 2; i <= 50; i++)    {        if (!vis[i]) prime.push_back(i);        for (int j = i + i; j <= 50; j += i)            vis[j] = 1;    }}LL C(int n, int m){    if (m > n - m) m = n - m;    LL res = 1;    for (int i = 0; i < m; i++)    {        res *= n - i;        res /= i + 1;    }    return res;}LL ans;int K, S;void dfs(int cur, int cnt, int now){    if (now * K > S) return ;    if (cur >= prime.size())    {        if (!cnt) return ;        if (cnt & 1) ans += C(S / now, K);        else ans -= C(S / now, K);        return ;    }    dfs(cur + 1, cnt + 1, now * prime[cur]);    dfs(cur + 1, cnt, now);}int main(){    //freopen("in.txt","r",stdin);    //freopen("out.txt","w",stdout);    init();    while (scanf("%d%d", &K, &S) != EOF)    {        ans = 0;        dfs(0, 0, 1);        printf("%lld\n", min(ans, 10000LL));    }    return 0;}
0 0
原创粉丝点击