题解报告——互质

【题目描述】

Shy 有 n 个数字,要求你从中选一个数和一群数,满足一群数的 GCD>1 且 GCD(一群数的 GCD,一个数的值)=1。问方案数。

【输入】

第一行一个整数 n。

第二行 n 个数表示数组。

【输出】

输出一个数表示答案。(mod 1e9+7)

【输入样例】

3
2 3 2

【输出样例】

5

【提示】

【数据规模】

对于 30%的数据,n≤10;

对于 100%的数据,1≤n≤500000, 2≤数字≤10000000。


【思路分析】

对于这道题,我们可以开一个桶,把每个含有当前因子的数存在这个桶里,记录个数。

那么不难想到,我们对于每个桶里的数进行计算,对于一群数,我们必定选择某个桶的数(因为这样gcd才不为1),所有组合为 2^cot[i]-1 种(二进制,除去全部不选的情况) ,那么选另外一个数的情况种类为n-cot[i],最终枚举gcd为i的情况种类为:

(2^cot[i]-1)*(n-cot[i])

但是,我们发现这个方法会使有的数被枚举多次,如pi=p*i,cot[pi]会在cot[p]与cot[i]时都被统计,那么可以发现对于一个合数,我们会在统计其因数时多算这个数的种类,那么就GG了,所以我们要筛掉多余的情况。


 

(分割线)

这里我们介绍两种容斥:

1.暴力容斥

对于每个数,我们倒序筛一次,把这个数的倍数的所有情况减去就是这个数的情况

1 for(i=1,j=2;i<=mx;++i,j=i<<1)while(j<=mx)cot[i]+=cot[j],j+=i;
2     for(i=2;i<=mx;++i)bak[i]=1ll*(p2[cot[i]]-1)*(n-cot[i])%mod;
3     for(i=mx;i>=2;--i)for(j=2;j*i<=mx;++j)bak[i]=(bak[i]-bak[j*i]+mod)%mod;
4     for(int i=2;i<=mx;++i)ans+=bak[i],ans%=mod;

 2.莫比乌斯容斥

我们注意到,对于gcd=xgcd=x 的方案,会被d|xd|x 算重,而莫比乌斯函数μ(x)μ(x) 正好具有如下性质:

 
d|x μ(d)=0


只要在求和的时候乘上μ(i)μ(i) ,就正好就把xx 及其约数的贡献(即d|xd|x )全部消掉了!跟上面的O(max×logemax)O(max×logemax) 算法是等价的。

 

但我们的计算是不包括d=1 的情况的,所以要把d=1 的情况剔除掉,因为μ(1)=1 ,所以有:

 
d|x,d1μ(x)=1

 

这样算出来整个d|x,d1 对答案的贡献是1的(因为 起来是1),所以我们最后要取反。

这样,容斥的复杂度变成了O(max),大大加快。

那为什么上面计算方案的公式为什么是对的呢?证明如下:

上面讲到了:ncot[i] 这一部分的数与cot[i] 里面的数的gcd 不一定为1,但这一部分被巧妙容斥掉了。

设一群数(cot[i] )的gcd=x,我们选择的数为y ,当前枚举的gcd=g ,为了方面描述,我们同时用x 表示正在讨论的一群数(cot[i] )的集合,考虑什么时候会计算上面的情况:

1.g|x 且g|y ,此时想xx 和yy 被分到了一组,都在我们选择的一群数里面,不会计算;

2.gx 且gy ,此时x,y 都不属于一群数,我们不会同时选择到它们两个;

3.g|x 且gy ,这个时候x 被包含进了一群数里,而y 是我们可能选择到的其他数,于是上面的情况会被计算一次;

4.gx 且g|y ,与上面类似,x 这个集合被算在了其他数里,y 被算在了一群数里,上面的情况又被计算一次;

经过上面的讨论,我们可以得到,当且仅当g 整除x,y 中的一方,而不整除另一方时,上面的不合法方案被计算。

但是,因为有d1|x μ(d1)=d2|y μ(d2)=0 ,即整除x 的μ(d1) 和为零,整除y 的μ(d2) 的和为零;又有d|x,d|μ(d)=0 ,即同时整除x,y 的数的μ(d) 之和也是零,根据容斥原理,只整除其中一方的数的μ 之和也为零。

综上,不合法的情况的μ 值之和为零,被消去。因此,本算法正确,由于本算法与上面的暴力容斥原理一致,故两个算法都正确。

【思路分析】

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int mod=1e9+7;
 5 const int N=1e7+5;
 6 int miu[N],num[N],p[N],cf[N],ans,mx,n;
 7 bool vis[N];
 8 void pre()
 9 {
10     for(int i=1;i<=mx;i++)
11     {
12         int j=i*2;
13         while(j<=mx) num[i]+=num[j],j+=i;
14     }
15     cf[0]=1;
16     for(int i=1;i<=n;i++)
17     cf[i]=(cf[i-1]*2)%mod;
18 }
19 void getmiu()
20 {
21     vis[1]=1,miu[1]=1;
22     for(int i=2;i<=mx;i++)
23     {
24         if(!vis[i]) p[++p[0]]=i,miu[i]=-1;
25         for(int j=1;j<=p[0];j++)
26         {
27             int t=p[j]*i;if(t>mx) break;vis[t]=1;
28             if(i%p[j]==0) {miu[t]=0;break;}
29             miu[t]=-miu[i];
30         }
31     }
32 }
33 int main()
34 {
35     scanf("%d",&n);
36     for(int i=1;i<=n;i++)
37     {
38         int a;
39         scanf("%d",&a);
40         num[a]++,mx=max(mx,a);
41     }
42     pre();getmiu();
43     for(int i=2;i<=mx;i++)
44     ans=(1ll*(cf[num[i]]-1)*(n-num[i])*miu[i]+ans)%mod;
45     ans=(-ans)%mod;if(ans<0) ans+=mod;
46     printf("%d",ans);
47     return 0;
48 }

 

转载于:https://www.cnblogs.com/genius777/p/9027062.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
探索全栈前端技术的魅力:HTML+CSS+JS+JQ+Bootstrap网站源码深度解析 在这个字化时代,构建一个既美观又功能强大的网站成为了许多开发者和企业追逐的目标。本份资源精心汇集了一套完整网站源码,融合了HTML的骨架搭建、CSS的视觉美化、JavaScript的交互逻辑、jQuery的高效操作以及Bootstrap的响应式设计,全方位揭秘了现代网页开发的精髓。 HTML,作为网页的基础,它构建了信息的框架;CSS则赋予网页生动的外观,让设计创意跃然屏上;JavaScript的加入,使网站拥有了灵动的交互体验;jQuery,作为JavaScript的强力辅助,简化了DOM操作与事件处理,让编码更为高效;而Bootstrap的融入,则确保了网站在不同设备上的完美呈现,响应式设计让访问无界限。 通过这份源码,你将: 学习如何高效组织HTML结构,提升页面加载速度与SEO友好度; 掌握CSS高级技巧,如Flexbox与Grid布局,打造适应各种屏幕的视觉盛宴; 理解JavaScript核心概念,动手实现动画、表单验证等动态效果; 利用jQuery插件快速增强用户体验,实现滑动效果、Ajax请求等; 深入Bootstrap框架,掌握移动优先的开发策略,响应式设计信手拈来。 无论是前端开发新手渴望系统学习,还是资深开发者寻求灵感与实用技巧,这份资源都是不可多得的宝藏。立即深入了解,开启你的全栈前端探索之旅,让每一个网页都成为技术与艺术的完美融合!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值