[atARC123F]Insert Addition
前置知识
下面,先来介绍一下Stern-Brocot Tree的结构:
其是一棵满二叉树,每一个节点都是一个最简分数,其中根为
假设前层的中序遍历分数依次为(其中,即根为第一层),并令且,那么第层的从左到右第个分数为(其中)
(特别的,根据定义在时,即仅包含和)
根据定义,不难得到有且,这可以方便理解下面的结论——
性质1:构成一个严格单调递增的序列(特别的,定义)
性质2:若,存在满足且当且仅当
性质3:若,存在满足当且仅当
性质1和性质2可以归纳得到,性质3可以根据性质2得到,证明过程略
题解
性质4:,有
性质5:,有
这两个性质同样可以归纳得到,证明过程略
结合性质1和3,不难得到且,并且对应的顺序即按照从小到大排序,由此即有一个的暴力做法
考虑优化,不妨假设(将除以即可),那么根据裴蜀定理,存在,并根据通解的形式调整使得,进而有
此时,不妨将二元组变为,并对条件分析——
性质6:
性质7:等价于
性质6可以归纳并结合辗转相减法得到,性质7直接展开可得,证明过程略
另外,关于的条件,将和用新二元组的表示,解得即且
综上,问题即求分母在中、值在中的从小到大第个最简分数(的分母),此时只需要在之前的Stern-Brocot Tree上二分即可
在过程中,即需要查询子树大小,也即求出值在中且分母的分数个数
关于这个问题,先将范围差分(即仅考虑),并对的条件容斥,即令为忽略此条件且分母的分数个数,那么不难得到答案即
预处理的前缀和后,数论分块即变为求个的值,则有,可以通过类欧几里得算法计算,时间复杂度为
注意到每层只有次上述查询,因此总共只有次查询
总复杂度即(其中),可以通过
(实际上跑不满,常数极小,甚至于一些比较暴力的代码都可以通过)

1 #include<bits/stdc++.h> 2 #include<atcoder/math> 3 using namespace std; 4 #define N 300005 5 #define ll long long 6 #define pii pair<int,int> 7 #define fi first 8 #define se second 9 vector<int>ans; 10 int n,aa,bb,vis[N],p[N],mu[N]; 11 ll l,r; 12 pii a,b; 13 bool cmp(pii x,pii y){ 14 return (ll)x.fi*y.se<(ll)x.se*y.fi; 15 } 16 ll get_g(pii k,int n){ 17 return atcoder::floor_sum(n,k.se,k.fi,k.fi+k.se); 18 } 19 ll get_tot(pii k){ 20 if (cmp(k,a))k=a; 21 if (cmp(b,k))k=b; 22 ll ans=0; 23 for(int i=1,j;i<=n;i=j+1){ 24 j=n/(n/i); 25 ans+=(ll)(mu[j]-mu[i-1])*get_g(k,n/i); 26 } 27 return ans; 28 } 29 void dfs(pii x,pii y){ 30 if ((cmp(b,x))||(cmp(y,a)))return; 31 pii m=make_pair(x.fi+y.fi,x.se+y.se); 32 if (m.se>n)return; 33 dfs(x,m); 34 if ((cmp(a,m))&&(cmp(m,b)))ans.push_back(m.se); 35 dfs(m,y); 36 } 37 void calc(pii x,pii y,ll l,ll r,ll s){ 38 if ((cmp(b,x))||(cmp(y,a))||(l>s)||(r<1))return; 39 if ((l<=1)&&(s<=r)){ 40 dfs(x,y); 41 return; 42 } 43 pii m=make_pair(x.fi+y.fi,x.se+y.se); 44 ll ss=get_tot(m)-get_tot(x); 45 calc(x,m,l,r,ss-1); 46 if ((l<=ss)&&(ss<=r)&&(cmp(a,m))&&(cmp(m,b)))ans.push_back(m.se); 47 calc(m,y,l-ss,r-ss,s-ss); 48 } 49 int main(){ 50 mu[1]=1; 51 for(int i=2;i<N;i++){ 52 if (!vis[i]){ 53 p[++p[0]]=i; 54 mu[i]=-1; 55 } 56 for(int j=1;(j<=p[0])&&(i*p[j]<N);j++){ 57 vis[i*p[j]]=1; 58 if (i%p[j])mu[i*p[j]]=mu[i]*mu[p[j]]; 59 else{ 60 mu[i*p[j]]=0; 61 break; 62 } 63 } 64 } 65 for(int i=1;i<N;i++)mu[i]+=mu[i-1]; 66 scanf("%d%d%d%lld%lld",&aa,&bb,&n,&l,&r); 67 int g=__gcd(aa,bb); 68 aa/=g,bb/=g,n/=g; 69 for(int i=0;i<aa;i++) 70 if (((ll)i*bb+1)%aa==0){ 71 a=make_pair(i,aa); 72 b=make_pair(((ll)i*bb+1)/aa,bb); 73 break; 74 } 75 if (l>1)l--; 76 else ans.push_back(a.se); 77 r--; 78 pii x=make_pair(0,1),y=make_pair(1,0); 79 calc(x,y,l,r,get_tot(y)-get_tot(x)-1); 80 if (get_tot(y)-get_tot(x)==r)ans.push_back(b.se); 81 for(int i=0;i<ans.size();i++)ans[i]*=g; 82 printf("%d",ans[0]); 83 for(int i=1;i<ans.size();i++)printf(" %d",ans[i]); 84 printf("\n"); 85 return 0; 86 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现