1. 程式人生 > 實用技巧 >題解 UVA12888 【Count LCM】

題解 UVA12888 【Count LCM】

Solution

簡化題意:求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m [\text{lcm}(i,j)==i\times j]\)

直接推柿子,由 \(\text{lcm}(i,j)=\dfrac{i\times j}{\gcd(i,j)}\),原式化為:\(\sum\limits_{i=1}^n\sum\limits_{j=1}^m [\dfrac{i\times j}{\gcd(i,j)}==i\times j]\) ,即 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m [\gcd(i,j)==1]\)

根據莫反,又化為 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{d|\gcd(i,j)}\mu(d)\)

然後可以改變列舉順序,先列舉因數 \(d\),得 \(\sum\limits_{d=1}\mu(d)\sum\limits_{i=1}^n[d|i]\sum\limits_{j=1}^m[d|j]\)

發現 \(\sum\limits_{i=1}^n[d|i]\) 是在求 \(1\sim n\)\(d\) 的倍數,可直接得 \(\lfloor \dfrac{n}{d} \rfloor\)。那麼原式又可變為 \(\sum\limits_{d=1}\mu(d)\lfloor \dfrac{n}{d} \rfloor\lfloor \dfrac{m}{d} \rfloor\)

這樣的式子顯然可以預處理 \(\mu\)

字首和然後整除分塊,總複雜度 \(\mathcal O(10^6+T\sqrt{\min(n,m)})\)

Code

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define TT template<typename T>
namespace io{static streambuf *inbuf=cin.rdbuf();static streambuf *outbuf=cout.rdbuf();char buf[1<<21],*p1=buf,*p2=buf;inline char gc(){return (p1==p2&&(p2=(p1=buf)+inbuf->sgetn(buf,1<<21),p1==p2)?EOF:*p1++);}inline void pc(const char x){static streambuf *outbuf=cout.rdbuf();outbuf->sputc(x);}
inline int read(){register int _s=0,_f=1;register char _ch=gc();for(;!isdigit(_ch);_ch=gc())if(_ch=='-')_f=-1;for(;isdigit(_ch);_ch=gc())_s=_s*10+_ch-'0';return _s*_f;}TT inline void read(T &x){x=0;register int _f=1;register char _ch=gc();for(;!isdigit(_ch);_ch=gc())if(_ch=='-')_f=-1;for(;isdigit(_ch);_ch=gc())x=x*10+_ch-'0';x*=_f;}
TT inline void write(T _x1){if(_x1<0)pc('-'),_x1=-_x1;static char _sta[15];int _p=0;do{_sta[_p++]=_x1%10^48;_x1/=10;}while(_x1);while(_p--)pc(_sta[_p]);}TT inline void writeln(const T _x){write(_x);pc(10);}TT inline void writesp(const T _x){write(_x);pc(' ');}}using namespace io;
//IO優化
const int maxn=1e6+5;
int n,m,mu[maxn],pr[maxn];
bool isp[maxn];
il void init(){//線性篩mobius字首和
    mu[1]=1;isp[1]=1;int tot=0;
    for(int i=2;i<=1e6;i++){
        if(!isp[i])pr[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot&&i*pr[j]<=1e6;j++){
            ll num=i*pr[j];
            isp[num]=1;
            if(i%pr[j]==0)break;
            mu[num]=-mu[i];
        }
    }for(int i=2;i<=1e6;i++)mu[i]+=mu[i-1];
}
il ll solve(ll x,ll y){//整除分塊
    ll i,j,res=0;//記得開ll
    for(i=1;i<=x&&i<=y;i=j+1){
        j=min(x/(x/i),y/(y/i));
        res+=1ll*(mu[j]-mu[i-1])*(x/i)*(y/i);
    }return res;
}
int main(){
    init();
    for(int T=read();T--;){
        n=read();m=read();
        writeln(solve(n,m));
    }
    return 0;
}