P3327 [SDOI2015] 约数个数和

[SDOI2015] 约数个数和

题目描述

d(x)x 的约数个数,给定 n,m,求

i=1nj=1md(ij)

输入格式

输入文件包含多组测试数据。
第一行,一个整数 T,表示测试数据的组数。
接下来的 T 行,每行两个整数 n,m

输出格式

T 行,每行一个整数,表示你所求的答案。

样例 #1

样例输入 #1

2
7 4
5 6

样例输出 #1

110
121

提示

【数据范围】
对于 100% 的数据,1T,n,m50000

链接

d(x)​的转化太艰难,考虑直接枚举约数进行统计。

d(ij)=xiyj[gcd(x,y)=1]
....要是推不出上面这个式子这题的式子就打不开,不太能做。。。抽象。
当知识点记下来吧。 其实还挺好理解的。其实问题就是我怎么知道我要推它。。看起来很合理,但是这是知道了这个是结果后觉得合理。在不知道它是结果前,真的能够知道要用它吗?

i=1nj=1md(ij),用上面的式子转化,得到i=1nj=1mxiyj[gcd(x,y)=1],再用ϵ(n)=d|nμ(d)
i=1nj=1mxiyjd|x,d|yμ(d)

i=1nj=1mxiyjd=1dmin(x,y)μ(d)[d|x][d|y]或者是i=1nj=1mxiyjd=1dmin(n,m)μ(d)[d|x][d|y],发现后面的写法di,j无关,可以直接提前最后的d的枚举

d=1min(n,m)μ(d)i=1nj=1mx|iy|j[d|gcd(x,y)],然后后面的部分被动的枚举检测改为直接统计贡献。其实就是转为考虑满足μ(d)能作为答案的是哪些部分

d=1min(n,m)μ(d)i=1ndndij=1mdmdj,整理一下就能得到前面的式子。
唯一的和莫反的板子的不同点就是d(ij)的转化。没了。不过后面的也不是完全常规,虽然思路是挺正常的。
后面的部分也不是普通的整除分块,循环本身是,内部又是,虽然两个重合起来不会脱离O(n)的复杂度,但是写起来好麻烦。。。

从结论的式子上看,这题就和前面的题目明显不一样了。有很多对于莫反的变换的新的理解。约数枚举的提前其实并不是一个非常套路的东西,它有本身的规则,我之前都是感性理解的,导致问题其实挺大,因为不一定正确。本质就是如果里面的约数枚举的内容没有和前面的变量枚举相关的,那就直接用分配律提前就好。
所以其实完全可以把限制作为一个乘积写出来而不是追求所谓的"简洁"而写在循环内部。感觉是没有任何好处和方便的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int a=0,b=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
	return a*b;
}
const int N=100000;
int prime[N+1],cnt,mu[N+1],vis[N+1],sum[N+1];ll f[N+1];
void init()
{
    mu[1]=1;
    for(int i=2;i<=N;i++)
    {
        if(!vis[i])prime[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&i*prime[j]<=N;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                
                break;
            }
            mu[i*prime[j]]=mu[i]*mu[prime[j]];
        }
    }
    for(int i=1;i<=N;i++)
    {
        sum[i]=sum[i-1]+mu[i];
    }
    for(int i=1;i<=N;i++)
    {
        for(int l=1,r=0;l<=i;l=r+1)
        {
            r=i/(i/l);
            f[i]+=1LL*(i/l)*(r-l+1);
        }
    }
}
int main()
{
    init();
    int T=read();
    while(T--)
    {
        int n=read(),m=read();
        ll ans=0;
        for(int l=1,r=0;l<=min(n,m);l=r+1)
        {
            r=min(n/(n/l),m/(m/l));
            ans+=(sum[r]-sum[l-1])*(f[n/l])*(f[m/l]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

居然卡我常...

posted @   HL_ZZP  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示