洛谷3327:约数个数和

洛谷3327:约数个数和

题意描述:

  • \(d(x)\)\(x\)的约数个数,给定\(n,m\)求:
  • \(\sum_{i=1}^n\sum_{j=1}^md(ij)\)\(T\)组数据。
  • 数据范围\(T,n,m\leq 50000\).

思路

  • \(d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\).
  • 所求就为:
  • \(\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\)
  • 枚举\(x,y\)有:
  • \(\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)=1]\sum_{i=1}^n\sum_{j=1}^m[x|n\ and\ y|m]\).
  • \(\sum_{x=1}^n\sum_{y=1}^m[gcd(x,y)=1]\frac{n}{x}\frac{m}{y}\)
  • 修改一下变量吧...:
  • \(\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=1]\frac{n}{i}\frac{m}{j}\).
  • 开始反演:
  • \(f(x)=\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=x]\frac{n}{i}\frac{m}{j}\).
  • 按照套路有:
  • \(F(x)=\sum_{x|d}f(d)=\sum_{i=1}^n\sum_{j=1}^m\frac{n}{i}\frac{m}{j}[x|gcd(i,j)]\)
  • 枚举\(ix,jx\),有
  • \(=\sum_{i=1}^{\frac{n}{x}}\sum_{j=1}^{\frac{m}{x}}\frac{n}{ix}\frac{m}{jx}\).
  • 这样就可以不用理会\(gcd(i,j)\)这个条件。
  • 有反演公式:
  • \(f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)\).
  • 又知道\(ans=f(1)\)
  • \(=\sum_{1|d}\mu(d)F(d)\)
  • \(=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{i=1}^{n/d}\frac{n}{di}\sum_{j=1}^{m/d}\frac{m}{dj}\).
  • 到了这里其实就可以做了,但是还有个小问题,就是如何快速的计算\(\sum_{i=1}^n\frac{n}{i}\)
  • 可以用整除分块来处理。
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 10;
int T, n, m;

bool vis[maxn];
int primes[maxn], mu[maxn], cnt, sum[maxn];
ll s[maxn];
void get_mu(int n)
{
    mu[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!vis[i])
        {
            primes[++cnt] = i;
            mu[i] = -1;
        }
        for(int j = 1; primes[j] <= n/i; j++)
        {
            vis[primes[j]*i] = 1;
            if(i % primes[j] == 0) break;
            else mu[i*primes[j]] = -mu[i];
        }
    }
    for(int i = 1; i <= n; i++)
        sum[i] = sum[i-1] + mu[i];
    for(int i = 1; i <= n; i++)
    {
        ll res = 0;
        for(int l = 1, r; l <= i; l = r + 1)
        {
            r = i/(i/l);
            res += 1ll*(r-l+1)*1ll*(i/l);
        }
        s[i] = res;
    }
}

inline void solve(int n, int m)
{
    if(n > m) swap(n, m);
    ll ans = 0;
    for(int l = 1, r; l <= n; l = r+1)
    {
        r = min(n/(n/l), m/(m/l));
        ans += 1ll*(sum[r]-sum[l-1])*1ll*s[n/l]*1ll*s[m/l];
    }
    printf("%lld\n", ans);
}

signed main()
{
    get_mu(50000);
    scanf("%lld", &T);
    while(T--)
    {
        scanf("%lld%lld", &n, &m);
        solve(n, m);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zxytxdy/p/12173434.html