2014 Super Training #3 H Tmutarakan Exams --容斥原理

原题: URAL 1091  http://acm.timus.ru/problem.aspx?space=1&num=1091

题意:要求找出K个不同的数字使他们有一个大于1的公约数,且所有的数字都不能大于一个指定的数字S。

解法:可以考虑每个S内的素数,此素数和它的所有倍数构成一个集合,则可以在这些集合中任意去k个元素,C(n,k)即为这种情况下的方法种数,比如K = 3,S = 10,

则可以形成3个集合: {2,4,6,8,10} , {3,6,9}, {5,10} ,第一个集合C(5,3),第二个集合C(3,3),第三个集合为0,同时可以看到,6在两个集合中出现了,所以要减去一个6的情况,这样就是容斥原理了。

枚举一个素数,两个素数即可,因为如果三个素数的话,公倍数至少为2x3x5 = 30, 此时S内是30的倍数只有一个(S<=50),就是30本身,此时要选K>=2个是不可能的。所以三个及以上可以不计。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
#define N 10007

ll C[55][55];
int prime[10] = {2,3,5,7,11,13,17,19,23};

void calc_C()
{
    memset(C,0,sizeof(C));
    C[1][0] = C[1][1] = 1;
    for(int i=2;i<=51;i++)
    {
        C[i][0] = 1;
        for(int j=1;j<=51;j++)
            C[i][j] = C[i-1][j] + C[i-1][j-1];
    }
}

int main()
{
    int k,S,i,j,res,num,flag;
    calc_C();
    while(scanf("%d%d",&k,&S)!=EOF)
    {
        ll res = 0;
        flag = 9;
        for(i=0;i<9;i++)
        {
            num = S/prime[i];
            if(num < k)
            {
                flag = i;
                break;
            }
            else
                res += C[num][k];
        }
        for(i=0;i<flag;i++)
        {
            for(j=i+1;j<flag;j++)
            {
                num = S/(prime[i]*prime[j]);
                if(num < k)
                    break;
                else
                    res -= C[num][k];
            }
        }
        printf("%lld
",min((ll)10000,res));
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/whatbeg/p/3815779.html