连分数 是实数作为有理数的特定收敛序列的表示。它们在算法竞赛(competitive programming)中很有用,因为它们易于计算,并且可以有效地用于在分母不超过给定值的所有数字中,找到基础实数(underlying real number)的最佳可能有理近似(best possible rational approximation)。
# return (x, y) such that Ax+By=C# assumes that such (x, y) existsdefdio(A,B,C):p,q=convergents(fraction(A,B))C//=A//p[-1]# divide by gcd(A, B)t=(-1)iflen(p)%2else1returnt*C*q[-2],-t*C*p[-2]
// returns [ah, ph, qh] such that points r[i]=(ph[i], qh[i]) constitute upper// convex hull of lattice points on 0 <= x <= N and 0 <= y <= r * x, where r =// [a0; a1, a2, ...] and there are ah[i]-1 integer points on the segment between// r[i] and r[i+1]autohull(autoa,intN){auto[p,q]=convergents(a);intt=N/q.back();vectorah={t};vectorph={0,t*p.back()};vectorqh={0,t*q.back()};for(inti=q.size()-1;i>=0;i--){if(i%2){while(qh.back()+q[i-1]<=N){t=(N-qh.back()-q[i-1])/q[i];intdp=p[i-1]+t*p[i];intdq=q[i-1]+t*q[i];intk=(N-qh.back())/dq;ah.push_back(k);ph.push_back(ph.back()+k*dp);qh.push_back(qh.back()+k*dq);}}}returnmake_tuple(ah,ph,qh);}
1 2 3 4 5 6 7 8 91011121314151617181920
# returns [ah, ph, qh] such that points r[i]=(ph[i], qh[i]) constitute upper convex hull# of lattice points on 0 <= x <= N and 0 <= y <= r * x, where r = [a0; a1, a2, ...]# and there are ah[i]-1 integer points on the segment between r[i] and r[i+1]defhull(a,N):p,q=convergents(a)t=N//q[-1]ah=[t]ph=[0,t*p[-1]]qh=[0,t*q[-1]]foriinreversed(range(len(q))):ifi%2==1:whileqh[-1]+q[i-1]<=N:t=(N-qh[-1]-q[i-1])//q[i]dp=p[i-1]+t*p[i]dq=q[i-1]+t*q[i]k=(N-qh[-1])//dqah.append(k)ph.append(ph[-1]+k*dp)qh.append(qh[-1]+k*dq)returnah,ph,qh
# (x, y) such that y = (A*x+B) // C,# Cy - Ax is max and 0 <= x <= N.defclosest(A,B,C,N):# y <= (A*x + B)/C <=> diff(x, y) <= Bdefdiff(x,y):returnC*y-A*xa=fraction(A,C)p,q=convergents(a)ph=[B//C]qh=[0]foriinrange(2,len(q)-1):ifi%2==0:whilediff(qh[-1]+q[i+1],ph[-1]+p[i+1])<=B:t=1+(diff(qh[-1]+q[i-1],ph[-1]+p[i-1])-B-1)//abs(diff(q[i],p[i]))dp=p[i-1]+t*p[i]dq=q[i-1]+t*q[i]k=(N-qh[-1])//dqifk==0:returnqh[-1],ph[-1]ifdiff(dq,dp)!=0:k=min(k,(B-diff(qh[-1],ph[-1]))//diff(dq,dp))qh.append(qh[-1]+k*dq)ph.append(ph[-1]+k*dp)returnqh[-1],ph[-1]defsolve(A,B,N):x,y=closest(A,N%A,B,N//A)returnN//A-x,y
// sum floor(k * x) for k in [1, N] and x = [a0; a1, a2, ...]intsum_floor(autoa,intN){N++;auto[ah,ph,qh]=hull(a,N);// The number of lattice points within a vertical right trapezoid// on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has// a+1 integer points on the segment (0; y1) - (dx; y2).autopicks=[](inty1,inty2,intdx,inta){intb=y1+y2+a+dx;intA=(y1+y2)*dx;return(A-b+2)/2+b-(y2+1);};intans=0;for(size_ti=1;i<qh.size();i++){ans+=picks(ph[i-1],ph[i],qh[i]-qh[i-1],ah[i-1]);}returnans-N;}
1 2 3 4 5 6 7 8 91011121314151617
# sum floor(k * x) for k in [1, N] and x = [a0; a1, a2, ...]defsum_floor(a,N):N+=1ah,ph,qh=hull(a,N)# The number of lattice points within a vertical right trapezoid# on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has# a+1 integer points on the segment (0; y1) - (dx; y2).defpicks(y1,y2,dx,a):b=y1+y2+a+dxA=(y1+y2)*dxreturn(A-b+2)//2+b-(y2+1)ans=0foriinrange(1,len(qh)):ans+=picks(ph[i-1],ph[i],qh[i]-qh[i-1],ah[i-1])returnans-N
# hull of lattice (x, y) such that C*y <= A*x+Bdefhull(A,B,C,N):defdiff(x,y):returnC*y-A*xa=fraction(A,C)p,q=convergents(a)ah=[]ph=[B//C]qh=[0]definsert(dq,dp):k=(N-qh[-1])//dqifdiff(dq,dp)>0:k=min(k,(B-diff(qh[-1],ph[-1]))//diff(dq,dp))ah.append(k)qh.append(qh[-1]+k*dq)ph.append(ph[-1]+k*dp)foriinrange(1,len(q)-1):ifi%2==0:whilediff(qh[-1]+q[i+1],ph[-1]+p[i+1])<=B:t=(B-diff(qh[-1]+q[i+1],ph[-1]+p[i+1]))//abs(diff(q[i],p[i]))dp=p[i+1]-t*p[i]dq=q[i+1]-t*q[i]ifdq<0orqh[-1]+dq>N:breakinsert(dq,dp)insert(q[-1],p[-1])foriinreversed(range(len(q))):ifi%2==1:whileqh[-1]+q[i-1]<=N:t=(N-qh[-1]-q[i-1])//q[i]dp=p[i-1]+t*p[i]dq=q[i-1]+t*q[i]insert(dq,dp)returnah,ph,qh
# find Q that minimizes Q*r mod m for 1 <= k <= n < mdefmod_min(r,n,m):a=fraction(r,m)p,q=convergents(a)foriinrange(2,len(q)):ifi%2==1and(i+1==len(q)orq[i+1]>n):t=(n-q[i-1])//q[i]returnq[i-1]+t*q[i]