[BZOJ3309]DZY Loves Math(莫比乌斯反演+线性筛)

本文介绍了一种处理与非积性函数相关的线性筛选的新思路,通过数学推导和算法实现,解决了特定数学问题,并附带了完整的C++代码。

=== ===

这里放传送门

=== ===

题解

先是喜闻乐见的画柿子阶段(设n为n和m之中较小者):

i=1nj=1mf(gcd(i,j))

i=1nj=1md=1n[(i,j)=d]f(d)

d=1nf(d)i=1nj=1m[(i,j)=d]

F(d) 为满足 1in1jm 并且 gcd(i,j)=d 的数字对数, g(d) 为满足 1in1jm 并且 d|gcd(i,j) 的数字对数
d=1nf(d)F(d)

d=1nf(d)d|Tμ(Td)g(T)

T=1nnTmTd|Tμ(Td)f(d)

那么关键就是如何对于所有的 T=1..n 求出 h(T)=d|Tμ(Td)f(d) 这个东西。可以看出 f(d) 并不是一个积性函数。。但是如果要快速的求出所有需要的 h(T) 好像也只能考虑一下线筛。。如果仍然按照以前的加入质因子的思路来考虑的话,因为 f 函数里面含着一个取max的操作,加入质因子以后没有办法知道哪些变了哪些没变,就非常GG。。

那么我们只能换一个思路来考虑。设 T=pa11pa22pa33...pakk ,选择的因数 d=pb11pb22pb33...pbkk 。显然满足 biai 。并且还有一个值得注意的地方是有贡献的 bi 应该满足 biai1 ,因为式子里面有一个 μ(Td) ,如果 bi ai1 还要小的话,就会在 Td 里面留下平方项,那么 μ 值就是0,对答案没有贡献。

接下来我们从 Td 的角度来考虑,对于一个质因子 pi ,称 bi=ai1 的时候为“选择了”这个质因子,相对的, bi=ai 的时候称为“没有选”这个质因子。显然每一种质因子的“选择”方案都唯一对应了一个合法的因数 d

因为f(d)的取值只和最大幂次的质因子有关,那么我们从这个角度来对 T=pa11pa22pa33...pakk 进行分析。如果 T 中存在ai aj 使得 aiaj ,那么我们以“是否等于 Maxa ”为标准将所有的 ai 划分为两个集合,其中 Maxa 是质因子的最大幂次。设 ai=Maxa 的集合为 A ,另一个集合为B。那么 f 函数的取值只和A集合中的选择方案有关,也就是当固定了 A 集合中的选取方案以后,f(d)的值是不会变的。这个时候看 B 集合中的数,它们可以随便选,对于每一个f(d)的取值,都有 2b 种选取方案,其中 b B集合中的元素个数。而因为固定了 A 集合中的选取方案,那么μ(Td)的值就和 B 中选取的元素个数的奇偶性有关。可以看出因为总的选取方案数目2b是个偶数,那么 B 中选取奇数个元素的方案数和选取偶数个元素的方案数相等。也就是μ值为1和-1的情况数相等。这说明对于 A 集合中每一个可能的选取方案,它都会被消成0。那么我们就得到了一个很有用的信息:当T中存在两个幂指数不相等的质因子时, h(T)=0

那么如果 T 中所有的幂指数ai都相等呢?这个时候总的选取方案数目为 2k ,可以看出仍然满足选奇数个元素的方案和选偶数个元素的方案相等。假如对于这所有的 2k 种选取方案, f(d) 都是相等的,那么它仍然会被消成0。但是实际上并不是这样,因为当所有质因数都“被选择”,也就是所有的 bi 都等于 ai1 的时候, f(d)=ai1 ,比别的方案的 f 值减少了1。也就是我们在统计的时候实际上在某一个f值中多加了一个1,而它前面的系数 μ(Td) 又是怎样的呢?显然它就是 T 中所有质因子每一种揪出来一个乘起来得到的结果,那么显然μ(Td)=(1)k。而因为多加了一个所以要把它减回来,也就是要累加一个 (1)k ,也就是 (1)k+1

那么只需要在线筛的时候记录最小质因子的指数 e[i] 和去掉最小质因子以后的部分 tmp[i] 就可以了。只要存在 e[i]e[tmp[i]] ,要筛的函数值就为0。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,m,prm[10000010],tail,g[10000010],e[10000010];
long long F[10000010],ans;
bool ext[10000010];
void get_prime(int N){
    for (int i=2;i<=N;i++){
        if (ext[i]==false){
            prm[++prm[0]]=i;
            F[i]=e[i]=1;g[i]=i;
        }
        for (int j=1;j<=prm[0];j++){
            if ((long long)i*prm[j]>N) break;
            ext[i*prm[j]]=true;
            if (i%prm[j]==0){
                g[i*prm[j]]=g[i]*prm[j];
                e[i*prm[j]]=e[i]+1;
                int tmp=i/g[i];
                if (tmp==1) F[i*prm[j]]=1;
                else F[i*prm[j]]=(e[tmp]==e[i*prm[j]])?-F[tmp]:0;
                break;
            }else{
                g[i*prm[j]]=prm[j];
                e[i*prm[j]]=1;
                F[i*prm[j]]=(e[i]==1)?-F[i]:0;
            }
        }
    }
    for (int i=1;i<=N;i++) F[i]+=F[i-1];
}
int main()
{
    scanf("%d",&T);
    get_prime(10000000);
    for (int wer=1;wer<=T;wer++){
        scanf("%d%d",&n,&m);ans=0;
        if (n>m) swap(n,m);
        for (int i=1;i<=n;i=tail+1){
            tail=min(n,min(n/(n/i),m/(m/i)));
            ans+=(long long)(n/i)*(long long)(m/i)*(F[tail]-F[i-1]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

偏偏在最后出现的补充说明

这道题在处理与非积性函数相关的线筛的时候用了和以前不同的思路,是一道值得注意的题目

评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值