P3327 [SDOI2015] 约数个数和

[SDOI2015] 约数个数和

题目描述

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

\[\sum_{i=1}^n\sum_{j=1}^md(ij) \]

输入格式

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

输出格式

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

样例 #1

样例输入 #1

2
7 4
5 6

样例输出 #1

110
121

提示

【数据范围】
对于 \(100\%\) 的数据,\(1\le T,n,m \le 50000\)

链接

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

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

\(\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^md(ij)\),用上面的式子转化,得到\(\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j} [\gcd(x,y)=1]\),再用\(\epsilon(n)=\displaystyle\sum_{d|n}\mu(d)\)
\(\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j} \displaystyle\sum_{d|x,d|y}\mu(d)\)

\(\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j} \displaystyle\sum_{d=1}^{d\leq min(x,y)}\mu(d)*[d|x]*[d|y]\)或者是\(\displaystyle\sum_{i=1}^{n}\displaystyle\sum_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j} \displaystyle\sum_{d=1}^{d\leq min(n,m)}\mu(d)*[d|x]*[d|y]\),发现后面的写法\(d\)\(i,j\)无关,可以直接提前最后的d的枚举

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

\(\displaystyle\sum_{d=1}^{min(n,m)}\mu(d)\displaystyle\sum_{i=1}^{\lfloor\frac n d\rfloor}\lfloor\frac n {di}\rfloor\displaystyle\sum_{j=1}^{\lfloor\frac m d\rfloor}\lfloor\frac m {dj}\rfloor\),整理一下就能得到前面的式子。
唯一的和莫反的板子的不同点就是\(d(ij)\)的转化。没了。不过后面的也不是完全常规,虽然思路是挺正常的。
后面的部分也不是普通的整除分块,循环本身是,内部又是,虽然两个重合起来不会脱离\(O(\sqrt 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 @ 2024-09-12 22:31  HL_ZZP  阅读(3)  评论(0编辑  收藏  举报