POJ 3904 Sky Code 解题报告(容斥原理)

来源:互联网 发布:购买汽车配件的软件 编辑:程序博客网 时间:2024/05/19 16:21

Sky Code
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 1294 Accepted: 390

Description

Stancu likes space travels but he is a poor software developer and will never be able to buy his own spacecraft. That is why he is preparing to steal the spacecraft of Petru. There is only one problem – Petru has locked the spacecraft with a sophisticated cryptosystem based on the ID numbers of the stars from the Milky Way Galaxy. For breaking the system Stancu has to check each subset of four stars such that the only common divisor of their numbers is 1. Nasty, isn’t it? Fortunately, Stancu has succeeded to limit the number of the interesting stars to N but, any way, the possible subsets of four stars can be too many. Help him to find their number and to decide if there is a chance to break the system.

Input

In the input file several test cases are given. For each test case on the first line the number N of interesting stars is given (1 ≤ N ≤ 10000). The second line of the test case contains the list of ID numbers of the interesting stars, separated by spaces. Each ID is a positive integer which is no greater than 10000. The input data terminate with the end of file.

Output

For each test case the program should print one line with the number of subsets with the asked property.

Sample Input

42 3 4 5 42 4 6 8 72 3 4 5 7 6 8

Sample Output

1 0 34

Source

Southeastern European Regional Programming Contest 2008

    解题报告: 个人觉得挺难的一道题。

    分析,题目让我们求的是互质4元组的数量,我们可以求出gcd(a, b, c, d)>1的4元组的数量,用C(n, 4)减去即为答案。

    听起来和之前几篇博客的题目都差不多,但这次的数据并不是连续唯一的,而是离散的。10000个数,1-10000。

    所以问题出现了。当计算素因子为2的数的组合时,如果遍历一遍复杂度就是10000,总复杂度就是10000*10000,这是不可接受的。

    在下不才,想不出什么比较好的方法,但是又不想看题解,所以决定打表。我们事先计算好1-10000每个数所有的因子数。注意,是因子。如果存在2这个因子,那么cnt[2]++。最后计算时,存在共因子2的四元组的数量就是C(cnt[2], 4),存在共因子3的四元组的数量就是C(cnt[3], 4),存在共因子2和3的四元组的数量就是C(cnt[2], 4)+C(cnt[3], 4)-C(cnt[6], 4)。此处使用容斥定理即可。

    当然,表太长,POJ不让交,故程序初始化时打表。用CodeBlocks测试了一下,打表的时间大概是250MS。POJ上总时间860MS。

    本次写此报告并不是说我的方法好,而是如果比赛的时候想不出什么优秀算法,想出不超时的方法一样能A题。当然,事后还是要学习一下大牛们好的算法,或者自己再想想,优化一下。

    笔者的代码如下:

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long LL;const int maxn = 10001;int deg[maxn], cnt[maxn];bool good[maxn];int table[maxn][65];LL c4(int v){    return (long long)v*(v-1)*(v-2)*(v-3)/24;}LL cal(){    memset(deg, 0, sizeof(deg));    memset(good, 1, sizeof(good));    LL ans = 0;    for(int i=2;i<maxn;i++)    {        if(good[i])        {            if(deg[i]==0) deg[i]=1;            LL t = cnt[i];            if(deg[i]&1)                ans+=c4(cnt[i]);            else                ans-=c4(cnt[i]);            for(int j=2;i*j<=maxn;j++)            {                if(deg[i]==1)                    if(j%i==0)                        good[i*j]=false;                    else                        deg[i*j]++;            }        }    }    return ans;}void init(){    for(int i=1;i<maxn;i++)    {        int top=0;        for(int j=2;j<=i;j++) if(i%j==0)            table[i][top++] = j;    }}int main(){    init();    int n;    while(~scanf("%d", &n))    {        memset(cnt, 0, sizeof(cnt));        for(int i=0;i<n;i++)        {            int tmp;            scanf("%d", &tmp);            for(int j=0;table[tmp][j];j++)                cnt[table[tmp][j]]++;        }        printf("%lld\n", c4(n) - cal());    }}


    搜了别人的解题报告,大家应该都是直接对每个数进行因式分解,然后奇偶也记录下来,最后一次完成统计求和。

    自己然后写的代码,时效110MS,代码如下:

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long LL;const int maxn = 10010;LL c4[maxn];int factor[maxn], prime[maxn];bool vis[maxn];void init(){    for(int i=4;i<maxn;i++)        c4[i] = (long long)i*(i-1)*(i-2)*(i-3)/24;}void decomposition(int n){    int top=0;    for(int i=2;i*i<=n;i++) if(n%i==0)    {        prime[top++] = i;        while(n%i==0) n/=i;    }    if(n>1) prime[top++] = n;    for(int i=1;i<(1<<top);i++)    {        int tmp = 1;        bool flag = false;        for(int j=0;j<top;j++) if(i&(1<<j))            tmp*=prime[j], flag = !flag;        factor[tmp]++;        vis[tmp]=flag;    }}int main(){    init();    int n;    while(~scanf("%d", &n))    {        LL ans = -c4[n];        memset(vis, 0, sizeof(vis));        memset(factor, 0, sizeof(factor));        for(int i=0;i<n;i++)        {            int tmp;            scanf("%d", &tmp);            decomposition(tmp);        }        for(int i=2;i<maxn;i++) if(factor[i])        {            if(vis[i])                ans+=c4[factor[i]];            else                ans-=c4[factor[i]];        }        printf("%lld\n", -ans);    }}

0 0
原创粉丝点击