@hdu - 6584@ Meteor
@description@
询问第 k 小的分子分母 ≤ n 的既约分数。
Input
第一行包含一个整数 T(T≤10^2),表示数据组数。
接下来 T 行每行两个整数 n, k,表示一组询问。保证询问的答案在 (0,1] 范围内。
Output
输出 T 行,每行包含一个分数 p/q,要求 gcd(p, q) = 1。
Sample Input
5
4 6
5 1
9 9
3 4
7 11
Sample Output
1/1
1/5
1/3
1/1
3/5
Hint
杭电支持 __int128。
@solution@
寻找第 k 小,不难想到使用二分法。
则假如二分到一个值 x,≤ x 的既约分数可以用如下式子计算:
\]
对其进行反演可得:
= \sum_{i=1}^{n}\sum_{d=1}^{d|i}\mu(d)*\lfloor\frac{x*i}{d}\rfloor \\
= \sum_{a*b\le n}\mu(a)*\lfloor x*b \rfloor \\
= \sum_{i=1}^{n}\mu(i)*\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}\lfloor x*j\rfloor\]
后面那一项如果以 j 为横坐标,x 为常数,可以看成直线 y = x*j 下的整点数量,联想到类欧几里得算法。
但是 x 是个实数的话类欧几里得是不可行的。不过假如我们二分直接使用分数进行二分的话,就的确可以使用类欧几里得算法。
加上预处理莫比乌斯函数前缀和 + 整除分块的话,可以在 \(O(\sqrt{n}*\log n)\) 的时间内完成一次计算。
实数二分需要一个迭代次数,那么我们多少次才能保证精度呢?
假如 a/b 与 c/d 都是两个分子分母 ≤ n 的不相等的分数,则 a/b - c/d = (ad - bc)/(b*d) 最小时,分子 ad - bc = 1,分母 b*d = n*n。
所以精度的一个上限是 1/(n*n),于是取 log(n*n) 次二分就可以保证精度。这样最大次数为 40。
下一个问题是,我们如何找到 > p/q 且最小的既约分数呢?
我们可以在 SB tree(Stern-Brocot Tree)上进行寻找。这棵树在具体数学上有介绍它的性质,WC2019 上也有讲一点。
这里有一篇简略的博客。
我们这道题只需要知道 SB Tree 的生成方式,以及它的几个性质:
(1)SB Tree 是一个既约分数的二叉搜索树。
(2)SB Tree 父亲的分母小于儿子的分母。
而这些足以让我们实现我们的目的。寻找答案的这个过程是 O(n)。
@accepted code@
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
typedef long long ll;
const int MAXN = 1000000;
__int128 gcd(__int128 a, __int128 b) {return (b == 0) ? a : gcd(b, a % b);}
struct frac{
__int128 a, b;
frac(__int128 _a=0, __int128 _b=0):a(_a), b(_b) {}
friend bool operator < (frac a, frac b) {return a.a*b.b < b.a*a.b;}
friend frac operator + (frac a, frac b) {
__int128 d = gcd(a.b*b.a + a.a*b.b, a.b*b.b);
return frac((a.b*b.a + a.a*b.b)/d, a.b*b.b/d);
}
friend frac operator / (frac a, __int128 k) {return frac(a.a, k*a.b);}
};
int n; ll k;
bool search_answer(frac p) {
frac ans = frac(1, 1), a = frac(0, 1), b = frac(1, 1);
while( a.b + b.b <= n ) {
frac c = frac(a.a + b.a, a.b + b.b);
if( p < c )
ans = b = c;
else a = c;
}
printf("%d/%d\n", int(ans.a), int(ans.b));
}
ll smiu[MAXN + 5];
__int128 func(__int128 A, __int128 B, __int128 C, __int128 n) {
if( A/C ) return func(A % C, B, C, n) + A/C*(n + 1)*n/2;
if( B/C ) return func(A, B % C, C, n) + B/C*(n + 1);
if( A == 0 ) return 0;
__int128 m = (A*n + B) / C;
if( m == 0 ) return 0;
return m*n - func(C, C - 1 - B, A, m - 1);
}
ll check(frac x) {
ll ans = 0; int p = 1, q = 1;
// printf("? %d %d\n", int(x.a), int(x.b));
while( p <= n ) {
q = p, p = (n/(n/q));
ll s = smiu[p] - smiu[q - 1];
ans += s*func(x.a, 0, x.b, n/p);
// printf("! %lld\n", ans);
p++;
}
return ans;
}
void solve() {
scanf("%d%lld", &n, &k);
int lim = log2(1LL*n*n) + 1;
frac le = frac(0, 1), ri = frac(1, 1);
for(int i=0;i<=lim;i++) {
frac mid = (le + ri) / 2;
if( check(mid) < k )
le = mid;
else ri = mid;
}
search_answer(le);
}
bool nprm[MAXN + 5];
void init() {
for(int i=1;i<=MAXN;i++)
smiu[i] = 1;
for(int i=2;i<=MAXN;i++) {
if( !nprm[i] ) {
for(int j=i;j<=MAXN;j+=i) {
nprm[j] = true;
if( (j/i) % i == 0 )
smiu[j] = 0;
else smiu[j] *= -1;
}
}
}
for(int i=1;i<=MAXN;i++)
smiu[i] += smiu[i-1];
}
int main() {
init();
int T; scanf("%d", &T);
while( T-- ) solve();
}
@details@
算是还不错的一道题,不过涉及到的知识点比较冷门。。。
我才不会说理解类欧几里得算法花了我一个晚上的时间。
找答案的时候还不能在树上递归找,不然会爆栈。正确的操作是迭代找答案。
@hdu - 6584@ Meteor的更多相关文章
- [2019HDU多校第一场][HDU 6584][G. Meteor]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6584 题目大意:求所有满足\(0<\frac{p}{q}\leq1, gcd(p,q)=1,p\ ...
- 【译】Meteor 新手教程:在排行榜上添加新特性
原文:http://danneu.com/posts/6-meteor-tutorial-for-fellow-noobs-adding-features-to-the-leaderboard-dem ...
- Using View and Data API with Meteor
By Daniel Du I have been studying Meteor these days, and find that Meteor is really a mind-blowing f ...
- HDOJ 2111. Saving HDU 贪心 结构体排序
Saving HDU Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- 【HDU 3037】Saving Beans Lucas定理模板
http://acm.hdu.edu.cn/showproblem.php?pid=3037 Lucas定理模板. 现在才写,noip滚粗前兆QAQ #include<cstdio> #i ...
- hdu 4859 海岸线 Bestcoder Round 1
http://acm.hdu.edu.cn/showproblem.php?pid=4859 题目大意: 在一个矩形周围都是海,这个矩形中有陆地,深海和浅海.浅海是可以填成陆地的. 求最多有多少条方格 ...
- HDU 4569 Special equations(取模)
Special equations Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u S ...
- HDU 4006The kth great number(K大数 +小顶堆)
The kth great number Time Limit:1000MS Memory Limit:65768KB 64bit IO Format:%I64d & %I64 ...
- HDU 1796How many integers can you find(容斥原理)
How many integers can you find Time Limit:5000MS Memory Limit:32768KB 64bit IO Format:%I64d ...
随机推荐
- python基础--字符编码以及文件操作
字符编码: 1.运行程序的三个核心硬件:cpu.内存.硬盘 任何一个程序要是想要运算,肯定是先从硬盘加载到当前的内存中,然后cpu根据指定的指令去执行操作 2.python解释器运行一个py文件的步骤 ...
- java项目小手册
集合了一些常用的小片段 1. 字符串有整型的相互转换 Java代码 String a = String.valueOf(2); //integer to numeric string int i = ...
- Leetcode61. Rotate List旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: 1->2->3->4->5->NULL, k = 2 输出: 4-& ...
- centos下nginx无法加载php文件,404
前提:html文件可以正常加载,php安装正常,nginx配置正确.仍然无法加载php文件,明明文件是存在,却报404,而不是直接输出文件 原因是:未启动php-fpm,未开启9000端口 首先查看是 ...
- Ajax--PHP+JQuery+Ajax解析json、XML数据、加载页面
一.JQuery+Ajax用get.post方式提交和请求数据 知识要点: $('#userName').blur(function () { var txt = $(this).val(); $.a ...
- ICP算法(迭代最近点)
参考博客:http://www.cnblogs.com/21207-iHome/p/6034462.html 最近在做点云匹配,需要用c++实现ICP算法,下面是简单理解,期待高手指正. ICP算法能 ...
- conda 下配置环境
conda list 查看已有环境(感觉anaconda 中查看的不全,只有指定路径的,但是这个路径和默认创建的路径不一样 然后我发现 要安装traits 库必须要的是PyQt4 但是我的py3下只有 ...
- layer弹出图片的问题
转载:https://blog.csdn.net/qq_41815146/article/details/81141088 layer下载地址:http://layer.layui.com/ jQue ...
- Permutations II 去掉重复的全排列
Given a collection of numbers that might contain duplicates, return all possible unique permutations ...
- 安装tomcat(fedora16)
sudo yum install tomcat6 sudo yum install tomcat6-webapps sudo yum install tomcat6-admin-webapps s ...