首页 写随笔

cdcq(本博客废弃!现用博客:https://www.cnblogs.com/cdcq/)

本博客废弃!现用博客:https://www.cnblogs.com/cdcq/

导航

【POJ3904】【P1202】水晶密码

说是莫比乌斯反演,其实只是玩儿玩儿内个miu函数而已……

原题:

 wty  打算攻击 applepi  的用来存放机密数据的水晶系统。 applepi 早有察觉,于是布置了一个密码系统来防备 wty 的攻击。 wty 经过研究发现,applepi 的密码系统中最关键的部分在于一 串四个正整数组成的密钥,四个正整数的顺序可以任意排列, 并且这四个正整数的最大公约数为 1。
wty 已经成功地把这四个正整数限制在了 N 个正整数构成的集合中,但是,密钥的数目 可能仍然是很庞大的。wty  希望知道有多少组可能的密钥。当然,applepi  已经挫败了 wty 的阴谋,但是他对这个问题也是饶有兴趣的。所以说,现在你需要帮助 applepi 算出有多少 组可能密钥,为 applepi 评估他的水晶系统的安全性提供参考。

N≤10000,集合中的数不大于 10000

 

题目要求四个数gcd为1,可以求出不为1的有几个,然后用总数减

先通过枚举数来求出共有num[i]个数含有因子i,c(num[i],4)即为gcd为i的情况个数,使用容斥去掉2*3和6这样的重复计算即可

手玩小数据可以发现,搞容斥的+或-的情况刚好和miu符合,比如2应该-,miu就是-1,6应该+,miu就是1之类的,所以就可以直接用miu来计算是过程变得更高端

核心代码:if(_num>=4)  ans+=miu[k]*_num*(_num-1)*(_num-2)*(_num-3)/24;

需要注意一点,求num[i]的时候直接枚举会T,要用sqrt优化,最后根据miu求ans的时候是直接从1枚举到maxx(最大的内个数),但是求num[i]的时候不能枚举到sqrt(maxx),而是枚举到sqrt(a[i])

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int read(){int z=0,mark=1;  char ch=getchar();
 8     while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;  ch=getchar();}
 9     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
10     return z*mark;
11 }
12 int n,a[51000];
13 bool kang[51000];  int zhi[51000],ztop=0;
14 int miu[51000];
15 int num[510000];
16 void get_miu(){
17     memset(kang,0,sizeof(kang));
18     miu[1]=1;
19     for(int i=2;i<=50000;i++){
20         if(!kang[i]){  zhi[++ztop]=i;  miu[i]=-1;}
21         for(int j=1;zhi[j]*i<=50000;j++){
22             kang[zhi[j]*i]=true;
23             if(i%zhi[j]==0){  miu[zhi[j]*i]=0;  break;}
24             miu[zhi[j]*i]=-miu[i];
25         }
26     }
27 }
28 int main(){//freopen("ddd.in","r",stdin);
29     get_miu();
30 while(scanf("%d",&n)!=EOF){
31     memset(num,0,sizeof(num));
32     int maxx=0;
33     for(int i=1;i<=n;i++){  a[i]=read();  maxx=max(maxx,a[i]);}
34     if(n<4){  cout<<0<<endl;  continue;}
35     for(int i=1;i<=n;i++){
36         int smax=int(sqrt(a[i]*1.0));
37         for(int j=1;j<=smax;j++)if(a[i]%j==0){
38             num[j]++;
39             if(a[i]/j!=j)  num[a[i]/j]++;
40         }
41     }
42     long long ans=0;
43     for(int k=1;k<=maxx;k++){
44         long long _num=num[k];
45         if(_num>=4)  ans+=miu[k]*_num*(_num-1)*(_num-2)*(_num-3)/24;
46     }
47     cout<<ans<<endl;
48 }
49     return 0;
50 }
View Code

 

posted on 2016-09-09 13:10  cdcq_old  阅读(409)  评论(0编辑  收藏  举报