[2018.05.05 T2] 互质2

本文详细解析了一道关于从n个数字中选出一部分使得这部分数字的GCD大于1且与另一个单独选出的数字互质的问题。通过枚举GCD并利用莫比乌斯函数进行优化,实现了高效求解。

暂无链接

互质2

【题目描述】

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。

题解

linners l i n n e r s 大佬是真的屌,直接A掉,蒟蒻%%%

容易想到直接枚举一群数是不科学的,所以我们尝试枚举 gcd g c d

首先,我们处理以 x x 为因子的数的个数,在这之前我们先开 107 10 7 的桶,统计每个数有多少个。显然,对于数 x x 2x,3x,...,nx 2 x , 3 x , . . . , n x 都是满足包含因数 x x 的,所以我们直接枚举 2x,3x,...,nx 2 x , 3 x , . . . , n x 暴力累加即可,设最大值为 max m a x ,该过程的复杂度为:

max1+max2+max3+...+maxmax=max×logemax m a x 1 + m a x 2 + m a x 3 + . . . + m a x m a x = m a x × l o g e m a x

当然,还有更暴力的直接 n n 枚举因数统计的,不过我们注意到数字的值域是 [2,107] [ 2 , 10 7 ] ,如果使用这种办法稳稳 TLE T L E

然后,对于一个因数 x x ,我们的方案数就是因数为 x x 的数排列组合一下再乘以一下其他的数的个数,因为要排除一个数都不选的情况,设因数包含 x x 的个数为 cot[x] c o t [ x ] ,所以最后的结果为:

(2cot[x]1)×(ncot[i]) ( 2 c o t [ x ] − 1 ) × ( n − c o t [ i ] )

其中 2cot[x]1 2 c o t [ x ] − 1 即为排除全部不选后的选择方案个数(显然),另外,建议预处理 2n 2 n ,面对 107 10 7 的数据快速幂可能不是很妙。

但是,很可能 ncot[i] n − c o t [ i ] 这一部分的数与 cot[i] c o t [ i ] 里面的数的 gcd g c d 不一定为1,放在这一部分的话不是很好解释,具体证明见分割线以下。

然后,我们这个结果肯定是算重了的,当我们算 gcd=x g c d = x 的时候,很可能其中的数的 gcd g c d 2x,3x,...,nx 2 x , 3 x , . . . , n x ,所以我们要从后往前将算重的部分剪掉(想想为什么),这个部分的复杂度也是 O(max×logemax) O ( m a x × l o g e m a x ) 的。

现在已经容斥完毕了,我们只需累加一遍答案即可,总复杂度 O(max×logemax) O ( m a x × l o g e m a x ) 5000ms 5000 m s 稳过不卡常。

——————————————–分割线————————————————

当然,容斥之神 linners l i n n e r s 是不会满足的。。。

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

d|xμ(d)=0 ∑ d | x μ ( d ) = 0

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

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

d|x,d1μ(x)=1 ∑ d | x , d ≠ 1 μ ( x ) = − 1

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

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

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

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

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

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

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

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

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

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

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

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

代码

代码巨简单 1000 bytesAC 1000 −   b y t e s A C 系列。

O(2×(max×logemax+max)) O ( 2 × ( m a x × l o g e m a x + m a x ) )
运行结果:

#include<bits/stdc++.h>
#define R register int
using namespace std;
const int M=1e7+5,mod=1e9+7;
int n,bak[M],cot[M],p2[M],mx;
void in()
{
    R i,a;
    scanf("%d",&n);p2[0]=1;
    for(i=1;i<=n;++i)p2[i]=p2[i-1]<<1,p2[i]%=mod;
    for(i=1;i<=n;++i)scanf("%d",&a),cot[a]++,mx=max(mx,a);
}
void ac()
{
    R i,j,ans=0;
    for(i=1,j=2;i<=mx;++i,j=i<<1)while(j<=mx)cot[i]+=cot[j],j+=i;
    for(i=2;i<=mx;++i)bak[i]=1ll*(p2[cot[i]]-1)*(n-cot[i])%mod;
    for(i=mx;i>=2;--i)for(j=2;j*i<=mx;++j)bak[i]=(bak[i]-bak[j*i]+mod)%mod;
    for(int i=2;i<=mx;++i)ans+=bak[i],ans%=mod;
    printf("%d",ans);
}
int  main()
{
    in();ac();
    return 0;
}

O(3max+max×logemax) O ( 3 m a x + m a x × l o g e m a x )
运行结果:

#include<bits/stdc++.h>
#define R register int
using namespace std;
const int M=1e7+5,mod=1e9+7;
int n,bak[M],cot[M],p2[M],miu[M],p[M],mx;
bool check[M];
void in()
{
    R i,a;
    scanf("%d",&n);p2[0]=1;
    for(i=1;i<=n;++i)p2[i]=p2[i-1]<<1,p2[i]%=mod;
    for(i=1;i<=n;++i)scanf("%d",&a),cot[a]++,mx=max(mx,a);
}
void getmiu()
{
    check[1]=1;miu[1]=1;
    R i,j,t;
    for(i=2;i<=mx;++i)
    {
        if(!check[i])p[++p[0]]=i,miu[i]=-1;
        for(j=1;j<=p[0];++j)
        {
            t=i*p[j];if(t>mx)break;check[t]=1;
            if(i%p[j]==0){miu[t]=0;break;}
            miu[t]=-miu[i];
        }
    }
}
void ac()
{
    getmiu();
    R i,j,ans=0;
    for(i=1,j=2;i<=mx;++i,j=i<<1)while(j<=mx)cot[i]+=cot[j],j+=i;
    for(i=2;i<=mx;++i)ans=(1ll*(p2[cot[i]]-1)*(n-cot[i])*miu[i]+ans)%mod;
    ans=(-ans)%mod;if(ans<0)ans+=mod;
    printf("%d",ans);
}
int  main()
{
    in();ac();
    return 0;
}
在充满仪式感的生活里,一款能传递心意的小工具总能带来意外惊喜。这款基于Java开发的满屏飘字弹幕工具,正是为热爱生活、乐于分享的你而来——它以简洁优雅的视觉效果,将治愈系文字化作灵动弹幕,在屏幕上缓缓流淌,既可以作为送给心仪之人的浪漫彩蛋,也能成为日常自娱自乐、舒缓心情的小确幸。 作为程序员献给crush的心意之作,工具的设计藏满了细节巧思。开发者基于Swing框架构建图形界面,实现了无边框全屏显示效果,搭配毛玻璃质感的弹幕窗口与圆润边角设计,让文字呈现既柔和又不突兀。弹幕内容精选了30条治愈系文案,从“秋天的风很温柔”到“你值得所有温柔”,涵盖生活感悟、自我关怀、浪漫告白等多个维度,每一条都能传递温暖力量;同时支持自定义修改文案库,你可以替换成专属情话、纪念文字或趣味梗,让弹幕更具个性化。 在视觉体验上,工具采用柔和色调生成算法,每一条弹幕都拥有独特的清新配色,搭配半透明渐变效果与平滑的移动动画,既不会遮挡屏幕内容,又能营造出灵动治愈的氛围。开发者还优化了弹幕的生成逻辑,支持自定义窗口大小、移动速度、生成间隔等参,最多可同时显示60条弹幕,且不会造成电脑卡顿;按下任意按键即可快速关闭程序,操作便捷无负担。 对于Java学习者而言,这款工具更是一份优质的实战参考。源码完整展示了Swing图形界面开发、定时器调度、动画绘制、颜色算法等核心技术,注释清晰、结构简洁,哪怕是初学者也能轻松理解。开发者在AI辅助的基础上,反复调试优化细节,解决了透明度控制、弹幕碰撞、资源占用等多个问题,这份“踩坑实录”也为同类项目开发提供了宝贵经验。 无论是想给喜欢的人制造浪漫惊喜,用满屏文字传递心意;还是想在工作间隙用治愈文案舒缓压力,或是作为Java学习的实战案例参考,这款满屏飘字弹幕工具都能满足你的需求。它没有复杂的操作流程,无需额外配置环境,下载即可运行,用最纯粹的设计传递最真挚的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值