bzoj 3994 约数个数和 —— 反演+数论分块
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3994
推导过程和这里一样:https://www.cnblogs.com/MashiroSky/p/6365020.html
不用取模但注意开 long long 。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=5e4+5; int cnt,pri[xn]; ll f[xn],mu[xn]; bool vis[xn]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void init() { int mx=5e4; mu[1]=1; for(int i=2;i<=mx;i++) { if(!vis[i])pri[++cnt]=i,mu[i]=-1; for(int j=1;j<=cnt&&(ll)i*pri[j]<=mx;j++) { vis[i*pri[j]]=1; if(i%pri[j])mu[i*pri[j]]=-mu[i]; else break; } } for(int i=2;i<=mx;i++)mu[i]+=mu[i-1]; for(int n=1;n<=mx;n++) for(int i=1,j;i<=n;i=j+1) { j=n/(n/i); f[n]+=(ll)(n/i)*(j-i+1);// } } int main() { int T=rd(); init(); while(T--) { int n=rd(),m=rd(); int mn=min(n,m); ll ans=0;// for(int i=1,j;i<=mn;i=j+1) { j=min(n/(n/i),m/(m/i)); ans+=(mu[j]-mu[i-1])*f[n/i]*f[m/i]; } printf("%lld\n",ans); } return 0; }