luoguP3455 [POI2007]ZAP-Queries

题意

\(f(n)=\sum\limits_{i=1}^{a}\sum\limits_{j=1}^{b}[gcd(i,j)==n],F(n)=\sum\limits_{n|d}f(d)\)

发现\(F(n)=\frac{a}{n}*\frac{b}{n}\),可以理解为对\(a\)以内的所有\(k*n\)都和\(b\)以内的\(k*n\)配对了一次。

由莫比乌斯反演:
\(f(n)=\sum\limits_{n|d}\mu(\frac{d}{n})F(d)\)
\(f(n)=\sum\limits_{n|d}\mu(\frac{d}{n})\frac{a}{d}*\frac{b}{d}\)
\(t=\frac{d}{n}\)
\(f(n)=\sum\limits_{t=1}^{min(\frac{a}{d},\frac{b}{d})}\mu(t)\frac{a}{t*n}\frac{b}{t*n}\)

\(ans=f(d)\)

除法分块即可做到\(O(n\sqrt{\min(a,b)})\)

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50010;
int T;
int mu[maxn],sum[maxn];
ll a,b,d;
bool vis[maxn];
vector<int>prime;
inline void shai(int n)
{
    vis[1]=1;mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])prime.push_back(i),mu[i]=-1;
        for(unsigned int j=0;j<prime.size()&&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];
        }
    }
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+mu[i];
}
inline ll solve(ll a,ll b,ll d)
{
    ll n=min(a/d,b/d),res=0;
    for(int l=1,r;l<=n;l=r+1)
    {
        r=min(a/(a/l),b/(b/l));
        res+=(a/(l*d))*(b/(l*d))*(sum[r]-sum[l-1]);
    }
    return res;
}
int main()
{
    shai(50000);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&a,&b,&d);
        printf("%lld\n",solve(a,b,d));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/nofind/p/11939837.html