#pragma once
#include<functional>
#include<utility>
#include<vector>usingnamespacestd;#include"stern-brocot-tree.hpp"// 下向き凸包の頂点列挙// (xl, yl) 始点, x in [xl, xr]// inside(x, y) : (x, y) が凸包内部か?// candicate(x, y, c, d) : (x, y) が凸包外部にあるとする。// 凸包内部の点 (x + sc, y + sd) が存在すればそのような s を返す// 存在しなければ任意の値 (-1 でもよい) を返すtemplate<typenameInt>vector<pair<Int,Int>>enumerate_convex(Intxl,Intyl,Intxr,constfunction<bool(Int,Int)>&inside,constfunction<Int(Int,Int,Int,Int)>&candicate){assert(xl<=xr);// inside かつ x <= xrautof=[&](Intx,Inty){returnx<=xr&&inside(x,y);};// (a, b) から (c, d) 方向に進めるだけ進むautogo=[&](Inta,Intb,Intc,Intd)->Int{assert(f(a,b));Intr=1,s=0;while(f(a+r*c,b+r*d))r*=2;while((r/=2)!=0){if(f(a+r*c,b+r*d))s+=r,a+=r*c,b+=r*d;}returns;};// (a, b) が out, (a + c * k, b + d * k) が in とする// out の間進めるだけ進むautogo2=[&](Inta,Intb,Intc,Intd,Intk){assert(!inside(a,b)andinside(a+c*k,b+d*k));Intok=0,ng=k;while(ok+1<ng){Intm=(ok+ng)/2;(inside(a+c*m,b+d*m)?ng:ok)=m;}returnok;};vector<pair<Int,Int>>ps;Intx=xl,y=yl;assert(inside(x,y)andgo(x,y,0,-1)==0);ps.emplace_back(x,y);while(x<xr){Inta,b;if(f(x+1,y)){a=1,b=0;}else{SternBrocotTreeNode<Int>sb;while(true){assert(f(x+sb.lx,y+sb.ly));assert(!f(x+sb.rx,y+sb.ry));if(f(x+sb.lx+sb.rx,y+sb.ly+sb.ry)){Ints=go(x+sb.lx,y+sb.ly,sb.rx,sb.ry);assert(s>0);sb.go_right(s);}else{Ints=candicate(x+sb.rx,y+sb.ry,sb.lx,sb.ly);if(s<=0||!inside(x+sb.lx*s+sb.rx,y+sb.ly*s+sb.ry)){a=sb.lx,b=sb.ly;break;}else{Intt=go2(x+sb.rx,y+sb.ry,sb.lx,sb.ly,s);sb.go_left(t);}}}}Ints=go(x,y,a,b);x+=a*s,y+=b*s;ps.emplace_back(x,y);}returnps;}
#line 2 "math/enumerate-convex.hpp"
#include<functional>
#include<utility>
#include<vector>usingnamespacestd;#line 2 "math/stern-brocot-tree.hpp"
#include<algorithm>
#include<cassert>
#line 6 "math/stern-brocot-tree.hpp"
usingnamespacestd;// x / y (x > 0, y > 0) を管理、デフォルトで 1 / 1// 入力が互いに素でない場合は gcd を取って格納// seq : (1, 1) から (x, y) へのパス。右の子が正/左の子が負template<typenameInt>structSternBrocotTreeNode{usingNode=SternBrocotTreeNode;Intlx,ly,x,y,rx,ry;vector<Int>seq;SternBrocotTreeNode():lx(0),ly(1),x(1),y(1),rx(1),ry(0){}SternBrocotTreeNode(IntX,IntY):SternBrocotTreeNode(){assert(1<=X&&1<=Y);Intg=gcd(X,Y);X/=g,Y/=g;while(min(X,Y)>0){if(X>Y){Intd=X/Y;X-=d*Y;go_right(d-(X==0?1:0));}else{Intd=Y/X;Y-=d*X;go_left(d-(Y==0?1:0));}}}SternBrocotTreeNode(constpair<Int,Int>&xy):SternBrocotTreeNode(xy.first,xy.second){}SternBrocotTreeNode(constvector<Int>&_seq):SternBrocotTreeNode(){for(constInt&d:_seq){assert(d!=0);if(d>0)go_right(d);if(d<0)go_left(d);}assert(seq==_seq);}// pair<Int, Int> 型で分数を getpair<Int,Int>get()const{returnmake_pair(x,y);}// 区間の下限pair<Int,Int>lower_bound()const{returnmake_pair(lx,ly);}// 区間の上限pair<Int,Int>upper_bound()const{returnmake_pair(rx,ry);}// 根からの深さIntdepth()const{Intres=0;for(auto&s:seq)res+=abs(s);returnres;}// 左の子に d 進むvoidgo_left(Intd=1){if(d<=0)return;if(seq.empty()orseq.back()>0)seq.push_back(0);seq.back()-=d;rx+=lx*d,ry+=ly*d;x=rx+lx,y=ry+ly;}// 右の子に d 進むvoidgo_right(Intd=1){if(d<=0)return;if(seq.empty()orseq.back()<0)seq.push_back(0);seq.back()+=d;lx+=rx*d,ly+=ry*d;x=rx+lx,y=ry+ly;}// 親の方向に d 進む// d 進めたら true, 進めなかったら false を返すboolgo_parent(Intd=1){if(d<=0)returntrue;while(d!=0){if(seq.empty())returnfalse;Intd2=min(d,abs(seq.back()));if(seq.back()>0){x-=rx*d2,y-=ry*d2;lx=x-rx,ly=y-ry;seq.back()-=d2;}else{x-=lx*d2,y-=ly*d2;rx=x-lx,ry=y-ly;seq.back()+=d2;}d-=d2;if(seq.back()==0)seq.pop_back();if(d2==Int{0})break;}returntrue;}// SBT 上の LCAstaticNodelca(constNode&lhs,constNode&rhs){Noden;for(inti=0;i<min<int>(lhs.seq.size(),rhs.seq.size());i++){Intval1=lhs.seq[i],val2=rhs.seq[i];if((val1<0)!=(val2<0))break;if(val1<0)n.go_left(min(-val1,-val2));if(val1>0)n.go_right(min(val1,val2));if(val1!=val2)break;}returnn;}friendostream&operator<<(ostream&os,constNode&rhs){os<<"\n";os<<"L : ( "<<rhs.lx<<", "<<rhs.ly<<" )\n";os<<"M : ( "<<rhs.x<<", "<<rhs.y<<" )\n";os<<"R : ( "<<rhs.rx<<", "<<rhs.ry<<" )\n";os<<"seq : {";for(auto&x:rhs.seq)os<<x<<", ";os<<"} \n";returnos;}friendbooloperator<(constNode&lhs,constNode&rhs){returnlhs.x*rhs.y<rhs.x*lhs.y;}friendbooloperator==(constNode&lhs,constNode&rhs){returnlhs.x==rhs.xandlhs.y==rhs.y;}};/**
* @brief Stern-Brocot Tree
*/#line 9 "math/enumerate-convex.hpp"
// 下向き凸包の頂点列挙// (xl, yl) 始点, x in [xl, xr]// inside(x, y) : (x, y) が凸包内部か?// candicate(x, y, c, d) : (x, y) が凸包外部にあるとする。// 凸包内部の点 (x + sc, y + sd) が存在すればそのような s を返す// 存在しなければ任意の値 (-1 でもよい) を返すtemplate<typenameInt>vector<pair<Int,Int>>enumerate_convex(Intxl,Intyl,Intxr,constfunction<bool(Int,Int)>&inside,constfunction<Int(Int,Int,Int,Int)>&candicate){assert(xl<=xr);// inside かつ x <= xrautof=[&](Intx,Inty){returnx<=xr&&inside(x,y);};// (a, b) から (c, d) 方向に進めるだけ進むautogo=[&](Inta,Intb,Intc,Intd)->Int{assert(f(a,b));Intr=1,s=0;while(f(a+r*c,b+r*d))r*=2;while((r/=2)!=0){if(f(a+r*c,b+r*d))s+=r,a+=r*c,b+=r*d;}returns;};// (a, b) が out, (a + c * k, b + d * k) が in とする// out の間進めるだけ進むautogo2=[&](Inta,Intb,Intc,Intd,Intk){assert(!inside(a,b)andinside(a+c*k,b+d*k));Intok=0,ng=k;while(ok+1<ng){Intm=(ok+ng)/2;(inside(a+c*m,b+d*m)?ng:ok)=m;}returnok;};vector<pair<Int,Int>>ps;Intx=xl,y=yl;assert(inside(x,y)andgo(x,y,0,-1)==0);ps.emplace_back(x,y);while(x<xr){Inta,b;if(f(x+1,y)){a=1,b=0;}else{SternBrocotTreeNode<Int>sb;while(true){assert(f(x+sb.lx,y+sb.ly));assert(!f(x+sb.rx,y+sb.ry));if(f(x+sb.lx+sb.rx,y+sb.ly+sb.ry)){Ints=go(x+sb.lx,y+sb.ly,sb.rx,sb.ry);assert(s>0);sb.go_right(s);}else{Ints=candicate(x+sb.rx,y+sb.ry,sb.lx,sb.ly);if(s<=0||!inside(x+sb.lx*s+sb.rx,y+sb.ly*s+sb.ry)){a=sb.lx,b=sb.ly;break;}else{Intt=go2(x+sb.rx,y+sb.ry,sb.lx,sb.ly,s);sb.go_left(t);}}}}Ints=go(x,y,a,b);x+=a*s,y+=b*s;ps.emplace_back(x,y);}returnps;}