BZOJ2671: Calc
Description
给出N,统计满足下面条件的数对(a,b)的个数:
1.1<=a<b<=N
2.a+b整除a*b
1.1<=a<b<=N
2.a+b整除a*b
Input
一行一个数N
Output
一行一个数表示答案
Sample Input
15
Sample Output
4
HINT
数据规模和约定
Test N Test N
1 <=10 11 <=5*10^7
2 <=50 12 <=10^8
3 <=10^3 13 <=2*10^8
4 <=5*10^3 14 <=3*10^8
5 <=2*10^4 15 <=5*10^8
6 <=2*10^5 16 <=10^9
7 <=2*10^6 17 <=10^9
8 <=10^7 18 <=2^31-1
9 <=2*10^7 19 <=2^31-1
10 <=3*10^7 20 <=2^31-1
题解Here!
这是一道神题。。。
所以我不会做。。。
只能去抄题解。。。
厚颜无耻写文。。。
题目要求:Ans=∑ni=1∑nj=1[(i+j)|(ij)]Ans=∑ni=1∑nj=1[(i+j)|(ij)]
我们把那个(i+j)|(ij)(i+j)|(ij)单独提出来看。
这个推导的过程可能比较扯淡复杂。。。
设d=gcd(i,j)d=gcd(i,j),那么原式即为d(id+jd)|(ijd2)d2d(id+jd)|(ijd2)d2
消去一个dd:(id+jd)|(ijd2)d(id+jd)|(ijd2)d
设u=id,v=jdu=id,v=jd,则:(u+v)|(uv)d(u+v)|(uv)d
而我们知道这时u+vu+v与uvuv已经没有公约数了,不能再消去了。
于是变成了:(u+v)|d(u+v)|d
神奇吧。。。
我们注意到(u+v)d<=n,u<=d,v<=d(u+v)d<=n,u<=d,v<=d,所以有:u,v∈[1,√n]u,v∈[1,√n]
所以题目就转化为:求√n∑u=1u−1∑v=1⌊nu+v⌋[gcd(u,v)==1]√n∑u=1u−1∑v=1⌊nu+v⌋[gcd(u,v)==1]
我们看到了熟悉的东西:[gcd(u,v)==1][gcd(u,v)==1]
莫比乌斯反演!
于是:√n∑d=1μ(d)⌊√nd⌋∑u=1u−1∑v=1⌊n(u+v)d2⌋√n∑d=1μ(d)⌊√nd⌋∑u=1u−1∑v=1⌊n(u+v)d2⌋
然后就可以愉快地跑了!
附代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #define MAXN 100010 using namespace std; long long n; int k=0,prime[MAXN],mu[MAXN]; bool np[MAXN]; inline long long read(){ long long date=0,w=1; char c=0; while (c< '0' ||c> '9' ){ if (c== '-' )w=-1;c= getchar ();} while (c>= '0' &&c<= '9' ){date=date*10+c- '0' ;c= getchar ();} return date*w; } void make(){ int m=MAXN-10; mu[1]=1; for ( int i=2;i<=m;i++){ if (!np[i]){ prime[++k]=i; mu[i]=-1; } for ( int j=1;j<=k&&prime[j]*i<=m;j++){ np[prime[j]*i]= true ; if (i%prime[j]==0) break ; mu[prime[j]*i]=-mu[i]; } } } long long calculate( int n, int m){ long long s=0; for ( int i=1;i<=m;i++){ int t=n/i; for ( int j=i+1,last;j<(i<<1)&&j<=t;j=last+1){ last=min((i<<1)-1,t/(t/j)); s+=1LL*(last-j+1)*(t/j); } } return s; } long long solve( long long n){ long long ans=0,m= sqrt (n); for ( int i=1;i<=m;i++)ans+=1LL*mu[i]*calculate(n/i/i,m/i); return ans; } int main(){ make(); n=read(); printf ( "%lld\n" ,solve(n)); return 0; } |
分类:
数论
, 数论——莫比乌斯反演
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· Linux系列:如何调试 malloc 的底层源码
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· 对象命名为何需要避免'-er'和'-or'后缀
· JDK 24 发布,新特性解读!
· .NET Core奇技淫巧之WinForm使用Python.NET并打包
· Java24你发任你发,我用Java8
· .NET 10 Preview 2 增强了 Blazor 和.NET MAUI