poj 3904 Sky Code【容斥原理】

简介: 点击打开链接 Sky Code Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1562   Accepted: 478 Description Stanc...

点击打开链接

Sky Code
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 1562   Accepted: 478

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

4
2 3 4 5 
4
2 4 6 8 
7
2 3 4 5 7 6 8

Sample Output

1 
0 
34
题目翻译:给出n(n<10000)个数,这些数<=10000,要求选出四个数字且他们的最大公约数为1的(注意:不需要两两互质),有多少种选法。

解题思路:本题可以先计算n个数字选四个总共有多少种选法,然后减去公约数不是1的。把共同具有某一素因子的数字划分到同一个集合,属于统一集合的四个数最大公约数一定大于1,如果四个数字不同时属于任何集合则最大公约数一定为1。用总的组合数减去那些四个数字同时被同一个集合包括了的组合数,所得结果即为最终答案。但是这些一个四个数字的组合可能同时属于若干个集合,因此需要在这些集合之间进行容斥原理,以求每个需要被减去的四个数字的组合都恰好被减掉一次。

首先,先将算法流程说一下,原理后面会有
Step 1:预处理,求组合数C(n,4),存于数组p中,方便下面运算!
Step 2:组合素数,用prime数组记录由m(m<=5)个素数相乘所得数的素因子
Step 3:读入当前数为a,将a分解为质因数形式,注意每个质因数只被记录一次
例如:  182=2*7*13 ———> m=3 so,相加
            2=2 ——>m=1 so,相加
            91=7*13 ——>m=2 so,相减
            注意12=2^2*3等这种是B=p1^k1*p2^k2+...这种有质因数指数不为一的合数记录不同因子的个数
            12=2*2*3 则 12会被分为2*3,多余的2被消去了,因此m=2;
Step 4:将分出的素数进行组合,并统计每种的方案数
例如:12=2^2*3——>12=2*3——>cnt[2]++,cnt[3]++,cnt[6]++
Step 5:处理之后,ans赋值为0
Step 6:for i 2-->10000
            if (m==奇数)    ans:=ans+C(cnt[i],4)
            else    ans:=and-C(cnt[i],4);
Step 7:输出ans
原理:首先考虑一个问题,1000以内6,7,8,9的倍数有多少个?答案是
1000div6+1000div7+1000div8+1000div9
-1000div(6*7)-1000div(6*8)-1000div(6*9)-1000div(7*8)-1000div(7*9)-1000div(8*9)
+1000div(6*7*8)+1000div(6*8*9)+1000div(7*8*9)
-1000div(6*7*8*9)
这是容斥原理的一个最简单的应用,类比这道题,Step3到4其实将每个数a的不重复约数记录了下来,
有公共约数的四个数的方案要从ans中减去,多减的要加上
显然m为奇时要减,m为偶时要加,这和”1000以内6,7,8,9的倍数有多少个?“这个问题奇偶是反的,
由于a最大为10000,所以m最大可以有5 (2*3*5*7*11<10000,2*3*5*7*11*13>10000)
  此表格解释利用2进制查找素因子组合

  1 2 4
1 T    
2   T  
3 T T  
4     T
5 T   T
6   T T
7 T T T

#include<iostream>
#include<stdio.h>
#include<string.h>
#define MAX 10005
using namespace std;
int cnt[MAX],num[MAX],prime[5];//数组不必过大,5个单位即可
long long  p[MAX]={0}; //必须用大整数
void Solve(int  n){
    int i,j,tol=0,k,sum;
    for(i=2;i*i<=n;i++){ //计算素因子个数
        if(n%i==0)
            prime[tol++]=i;
        while(n%i==0)//去重复因子
            n/=i;
    }
    if(n!=1) prime[tol++]=n;    //如果本身就是大于n开方的素数,需要加一,这点不要忘记
    for(i=1;i<(1<<tol);i++){  //总共有1~2^tol-1个组合
        k=1,sum=0;
        for(j=0;j<tol;j++)//巧妙利用二进制来查找到所有素因子组合构成的数
            if(i&(1<<j)){
                k*=prime[j];
                sum++;
            }
        cnt[k]++; //记录含有因子K的数的个数,比如n=30,则cnt[2]++,cnt[3]++,cnt[5]++;
                                            //cnt[6]++,cnt[10]++,cnt[15]++;cnt[30]++;
        num[k]=sum;//记录k中含有素因子的个数 num[2]=1,num[3]=1,num[5]=1,num[6]=2,
    }                                       //num[10]=2,num[15]=2,num[30]=3,
}
int main(){
   int m,n;
   long long i;
   for(i=4;i<MAX;i++)        //先打表,提高效率,i<4时p[i]为0;
        p[i]=i*(i-1)*(i-2)*(i-3)/24;
   while(~scanf("%d",&n)){
       memset(cnt,0,sizeof(cnt));
       for(i=0;i<n;i++){
           scanf("%d",&m);
           Solve(m);        //求解其素因子,并统计相关数据
       }
       long long ans=0;
       for(i=0;i<MAX;i++)
           if(cnt[i]>=4)//剪枝,必须大于等于四
               if(num[i]&1) //假如含有素因子个数为奇数,则加上;否则减去
                    ans+=p[cnt[i]];
               else
                    ans-=p[cnt[i]];
       cout<<p[n]-ans<<endl; //最后用总的减去不符合的四元组个数
   }return 0;
}

目录
相关文章
|
8月前
|
算法
算法每日一题---两数之和
算法每日一题---两数之和
34 0
|
存储
poj 3254 Corn Fields (状态压缩dp)
状态压缩dp其实就是用二进制来表示所有的状态,比如这题, 我们在某一行可以这样取0 1 0 1 1 0 1,用1代表取了,0代表没取,因为这点,它的数据量也限制在20以内,所有看到这样数据量的题目可以先考虑一下状态压缩dp。对于有多行的数据,所有状态的总数必然很庞大,而且不用特殊的方法想要存储这些状态是不太现实的。既然每个点只有这两种情况,我们可以用二进制的一位来表示,0 1 0 1 1 0 1就可以表示为二进制0101101也就是十进制的45,如果我们想要枚举6个点的所有状态,我们只需要从0到2^6取其二进制就可以了,并不会重复或是多余。
36 0
|
C语言
Leetcode---爬楼梯
Leetcode---爬楼梯
34 0
每日一题---29. 两数相除[力扣][Go]
每日一题---29. 两数相除[力扣][Go]
每日一题---29. 两数相除[力扣][Go]
每日一题 --- 537. 复数乘法[力扣][Go]
每日一题 --- 537. 复数乘法[力扣][Go]
每日一题 --- 537. 复数乘法[力扣][Go]
每日一题---16. 最接近的三数之和[力扣][Go]
每日一题---16. 最接近的三数之和[力扣][Go]
每日一题---16. 最接近的三数之和[力扣][Go]
每日一题---13. 罗马数字转整数[力扣][Go]
每日一题---13. 罗马数字转整数[力扣][Go]
每日一题---13. 罗马数字转整数[力扣][Go]
每日一题---12. 整数转罗马数字[力扣][Go]
每日一题---12. 整数转罗马数字[力扣][Go]
每日一题---12. 整数转罗马数字[力扣][Go]
AC牛客 BM97 旋转数组
AC牛客 BM97 旋转数组
96 0
|
存储 算法
算法题每日一练---第71天:回文数
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
170 0
算法题每日一练---第71天:回文数