杭电多校第一场1007Meteor(类欧几里得&莫比乌斯反演&Stern-Brocot树&分数二分 )

杭电多校第一场1007Meteor(类欧几里得&莫比乌斯反演&Stern-Brocot树&分数二分 )

题目大意

求分子分母均小于n且互素的第K小的数字

解题思路

[外链图片转存失败(img-gZpJqObO-1563891926457)(C:\Users\82339\AppData\Roaming\Typora\typora-user-images\1563891831660.png)]

AC代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
struct Frac{
	LL son,mon;
	Frac(LL sons,LL mons){
		son=sons/__gcd(sons,mons);
		mon=mons/__gcd(sons,mons);
	}
	Frac(){}
	Frac friend operator+(Frac x,Frac y)
	{
		LL nmon=x.mon/__gcd(x.mon,y.mon)*y.mon;
		LL nson=nmon/x.mon*x.son+nmon/y.mon*y.son;
		LL g=__gcd(nmon,nson);
		return Frac(nson/g,nmon/g);
	}
	Frac friend operator/(Frac x,LL y)
	{
		LL nmon=x.mon*y;
		LL nson=x.son;
		LL g=__gcd(nson,nmon);
		return Frac(nson/g,nmon/g);
	}
	bool friend operator<=(Frac x,Frac y)
	{
		LL nmon=x.mon/__gcd(x.mon,y.mon)*y.mon;
		LL sonx=nmon/x.mon*x.son,sony=nmon/y.mon*y.son;
		return sonx<=sony;
	}
};
#define LL __int128
long long Sum(LL n,LL a,LL b,LL c)
{
	
	if(n<=0) return 0;
	LL ans=0;
	if(c<0)
	{
		LL t=(a-c-1)/a;
		c+=a*t;
		ans-=n*t;
	}
	if(b<0)
	{
		LL t=(a-1-b)/a;
		b+=a*t;
		ans-=(n+1)*n*t/2;
	}
	if(c/a>0||b/a>0)
	{
		ans+=(c/a)*n;ans+=(b/a)*n*(n+1)/2;
		c%=a;b%=a;
	}
	LL newn=(b*n+c)/a;
	ans+=newn*(n+1)-Sum(newn,b,a,-c+b-1);
	return ans;
}
#undef LL
int n,k;	
const int size=1e6+5;
int p[size];bool prime[size];
int mu[size];int tot=0;
int summu[size];
void init()
{
	for(int i=1;i<size;i++)	prime[i]=true;
	mu[1]=1;
	for(int i=2;i<size;i++)
	{
		if(prime[i])
		{
			p[++tot]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=tot&&p[j]*i<size;j++)
		{
			prime[i*p[j]]=false;
			if(i%p[j]==0)
			{
				mu[i*p[j]]=0;
				break ;
			}
			else mu[i*p[j]]=-mu[i];
		}
	}
	summu[0]=0;
	for(int i=1;i<size;i++) summu[i]=summu[i-1]+mu[i];
}
LL count(Frac f)
{
	LL ans=0;
	for(int l=1,r;l<=n;l=r+1)
	{
		r=n/(n/l);
		ans=ans+1LL*(summu[r]-summu[l-1])*Sum(n/l,f.mon,f.son,0);
	}
	return ans;	
}
Frac SBtree(Frac up)
{
	Frac L=Frac(0,1),R=Frac(1,0);
	LL x=up.son,y=up.mon;
	Frac mid(1,1);
	Frac ans=Frac(1,1);
	while(x!=y&&mid.son<=n&&mid.mon<=n)
	{
//		cout<<mid.son<<' '<<mid.mon<<endl;
		if(mid.son*up.mon>mid.mon*up.son) ans=mid;
		if(x<y)
		{
			R=mid;
			mid=Frac(L.son+R.son,L.mon+R.mon);
			y-=x;
		}
		else
		{
			L=mid;
			mid=Frac(L.son+R.son,L.mon+R.mon);
			x-=y;
		}
	}
	return ans;
}
int32_t main()
{
	int t;
	init();
	scanf("%lld",&t);
	while(t--)
	{
	 	scanf("%lld%lld",&n,&k);
	 	Frac l(0,1),r(1,1);
	 	Frac ans(-1,-1);
	 	for(int t=40;t--;) 
	 	{
	 		Frac mid=(l+r)/2;
	 		if(count(mid)<k)
	 		{
	 			ans=mid;
	 			l=mid;
	 		}else r=mid;
	 	}
//	 	cout<<ans.son<<' '<<ans.mon<<endl;
	 	Frac fans=SBtree(ans);
	 	printf("%lld/%lld\n",fans.son,fans.mon);
	}
}
/*
5
4 6
5 1
9 9
3 4
7 11
*/ 
 	}

// cout<<ans.son<<’ '<<ans.mon<<endl;
Frac fans=SBtree(ans);
printf("%lld/%lld\n",fans.son,fans.mon);
}
}
/*
5
4 6
5 1
9 9
3 4
7 11
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值