題解 UVA12888 【Count LCM】
阿新 • • 發佈:2020-10-31
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\)
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; }