51nod1222最小公倍数计数

时间:2023-11-11 13:09:08

51nod1222

http://210.33.19.103/contest/1113/problem/2

同学的神仙做法:

首先考虑先去掉X<=Y的限制,也就是先计算满足要求的任意有序pair(X,Y)的数量,再用一些简单操作(略去)得到目标答案

化简式子可以得到$\sum_{d}\mu(d)\sum_a\sum_b\sum_c[abc<={\lfloor}\frac{n}{d^2}{\rfloor}]$

可以强行给a,b,c规定一个顺序。设a是最小的,则a只需要枚举到$(\frac{n}{d^2})^{1/3}$就行

剩下有很多做法了,略去了

 %:pragma GCC optimize()
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
int mu[];
int prime[],len;
bool nprime[];
/*
ll calc0(ll n)
{
ll d,i,j,k,tan1,tan2,tan3,t1;
for(d=1;;++d)
{
if(d*d>n) break;
t1=n/(d*d);
tan1=tan2=tan3=0;
for(i=1;i<=100;++i)
for(j=1;j<=100;++j)
for(k=1;k<=100;++k)
if(i*j*k<=t1)
if(i==j&&j==k)
++tan3;
else if(i==j||j==k||i==k)
++tan2;
else ++tan1;
printf("at%lld %lld %lld\n",tan1,tan2,tan3);
}
return 233;
}
*/
ll calc(ll n)
{
ll d,t1,t2,endj,i,j,j1,tan1,tan2,tan3,ans=,endi;
for(d=;;++d)
if(mu[d])
{
if(d*d>n) break;
t1=n/(d*d);
tan1=tan2=tan3=;
for(i=;;++i)
{
if(i*i*i>t1) break;
t2=t1/i;
endj=ll(sqrt(t2+0.5));
for(j=i+;j<=endj;j=j1+)
{
j1=min(endj,t2/(t2/j));
tan1+=(t2/j)*(j1-j+);
}
if(i+<=endj)
tan1-=(endj+i+)*(endj-i)/;
}
tan1*=;
endi=ll(sqrt(t1+0.5));
for(i=;i<=endi;++i)
tan2+=t1/(i*i);
for(i=;;++i)
{
if(i*i*i>t1) break;
++tan3;
}
tan2=(tan2-tan3)*;
ans+=mu[d]*(tan1+tan2+tan3);
//printf("1t%lld %lld %lld %lld\n",d,tan1,tan2,tan3);
}
return ans;
}
int main()
{
ll i,j,t;
mu[]=;
for(i=;i<=;i++)
{
if(!nprime[i]) prime[++len]=i,mu[i]=-;
for(j=;j<=len&&(t=i*prime[j])<=;j++)
{
nprime[t]=;
if(i%prime[j]==) {mu[t]=;break;}
else mu[t]=-mu[i];
}
}
ll a,b;
scanf("%lld%lld",&a,&b);
printf("%lld\n",(calc(b)-calc(a-)+b-a+)/);
//calc0(b);calc0(a-1);
return ;
}