//generated by GPT-5.6 Sol modified by hamamu namespace BITSpace{ using ll = long long; using Int = long long; // デフォルトの加算・減算 template struct BITAdd{ T operator()(const T &a,const T &b)const{ return a+b; } }; template struct BITSub{ T operator()(const T &a,const T &b)const{ return a-b; } }; template,class Sub=BITSub> struct BIT{ // sub=nullptr_t のとき、逆演算が必要な関数を requires で制限する。 static constexpr bool HAS_SUB=!is_same_v; ll n=0,st=0; // 添字数と公開添字の基準 T e; // 集約の単位元 Ope op; // 値同士の合成 Sub sub; // 累積値から区間値を取り出す逆演算 // bit[i]は1-origin 公開添字iはbit[i-st+1]側に対応 vector bit; // type_identity_tで型推論対象から外れ、0LLと書く必要がなくなる BIT(T,ll n,const type_identity_t &e=T()):n(n),e(e),op(Ope()),sub(Sub()){ init(n); } BIT(T,ll n,const type_identity_t &e,Ope op,Sub sub):n(n),e(e),op(move(op)),sub(move(sub)){ init(n); } BIT(T,const vector &ini,const type_identity_t &e=T()):n((ll)ini.size()),e(e),op(Ope()),sub(Sub()){ init(ini); } BIT(T,const vector &ini,const type_identity_t &e,Ope op,Sub sub):n((ll)ini.size()),e(e),op(move(op)),sub(move(sub)){ init(ini); } void setOffset(ll st){ this->st=st; } Int St()const{ return (Int)st; } Int En()const{ return (Int)(st+n-1); } template bool in(I... i)const{ return ((st<=i && i<=st+n-1) && ...); } // 先頭の1要素は1-origin用のダミー。 void init(ll n){ assert(n>=0); this->n=n; bit.assign(n+1,e); } void init(const vector &ini){ init((ll)ini.size()); //iniを葉として入れ、各要素を一度だけ親へ集約 for(ll i=0;i toVector()const requires(HAS_SUB){ vector a; for(ll k=0;k0;j-=j&-j) ret=op(ret,bit[j]); return ret; } T total()const{ return prefix(st+n-1); } //---- BIT上二分探索 pair floorGroup(ll l,T x)const requires(HAS_SUB); pair ceilGroup(ll l,T x)const requires(HAS_SUB); pair floorGroupRev(ll r,T x)const requires(HAS_SUB); pair ceilGroupRev(ll r,T x)const requires(HAS_SUB); //---- 多重集合用API void insert(ll x) requires(HAS_SUB); bool erase(ll x) requires(HAS_SUB); bool contains(ll x)const requires(HAS_SUB); Int leftnumof(ll x)const requires(HAS_SUB); Int rightnumof(ll x)const requires(HAS_SUB); Int rank(ll x)const requires(HAS_SUB); Int rrank(ll x)const requires(HAS_SUB); Int nthsmall(ll r)const requires(HAS_SUB); Int nthbig(ll r)const requires(HAS_SUB); //---- 動的imos用API void rangeadd(ll l,ll r,const T &x) requires(HAS_SUB); T imos(ll i)const; //---- 01列用API Int count(ll b,ll l,ll r)const requires(HAS_SUB); Int ceilIdxOf(ll b,ll i,ll d=1)const requires(HAS_SUB); Int floorIdxOf(ll b,ll i,ll d=1)const requires(HAS_SUB); void flip(ll i) requires(HAS_SUB); pair runRange(ll i)const requires(HAS_SUB); bool isAll(ll b,ll l,ll r)const requires(HAS_SUB); bool isAny(ll b,ll l,ll r)const requires(HAS_SUB); private: ll select01(ll b,ll k)const; // P[p]=op(A[0],...,A[p-1]) とする。 // 単調な条件 ok(p,P[p]) を満たす最大 p と P[p] を binary lifting で求める。なければp=-1。 template pair maxPrefix(F ok)const{ if(!ok(0,e)) return {-1,e}; ll p=0; T cur=e; for(ll step=(ll)bit_floor((unsigned long long)n);step;step>>=1){ ll np=p+step; if(np<=n){ T nxt=op(cur,bit[np]); // 条件を保つ区間だけ採用し、次の小さいstepへ進む。 if(ok(np,nxt)) p=np,cur=move(nxt); } } return {p,cur}; } }; } using BITSpace::BIT; /* - ---- 定義 ---- BIT bit(T(),N); //データ型T、[0,N-1]、初期値0(正確にはT())、演算は加減算 BIT bit(T(),ini); //第2引数iniならO(N)構築 BIT bit(T(),Nかini,e); //単位元e指定 BIT bit(T(),Nかini,e,op,sub); //op指定,逆演算sub有 BIT bit(T(),Nかini,e,op,nullptr);//op指定,逆演算sub無 一部操作不可となる //op=maxの例 BIT bit(ll(),ini,-INF,[](ll a,ll b){return max(a,b);},nullptr); //op=XORの例 BIT bit(ll(),ini,0, [](ll a,ll b){return a^b;}, //op [](ll a,ll b){return a^b;}); //sub - ---- 範囲関連 ---- bit.setOffset(st); //添字の基準を変更 範囲が[st,st+N-1]になる ll l=bit.St(),r=bit.En(); //現在の範囲[st,st+N-1]。N=0ならEn()==St()-1 bool a=bit.in(i,j,…可変引数); //true:すべて範囲内 - ---- 操作 ---- bit.init(N); //サイズNでクリア。stは維持 bit.init(ini); //iniで再構築。stは維持 bit.apply(i,x);//A[i]<-op(A[i],x) bit.set(i,x); //A[i]<-x sub有時のみ - ---- 取得 ---- [St(),En()]外は単位元として扱う ll x=bit.get(i); //A[i] sub有時のみ ll x=bit[i]; //A[i] sub有時のみ ll x=bit.get(l,r); //[l,r] sub有時のみ ll x=bit.prefix(r);//[St(),r]。rEn()なら全体の集約値 T x=bit.total(); //全要素の集約値 vll a=bit.toVector(); //a[k]=A[St()+k] sub有時のみ if(bit.n) dumpNd("bit",bit.toVector(),args().sx(bit.St()).labels({"i"})); //dump 以下は使用時別途貼る。 - ---- BIT上二分探索 ---- `bitsearch` - ---- 多重集合用API ---- `bitmultiset` - ---- 動的imos用API ---- `bitimos` - ---- 01列用API ---- `bitbinary` */ /***************************/ /****** BIT上二分探索 ******/ /***************************/ namespace BITSpace{ template pair BIT::floorGroup(ll l,T x)const requires(HAS_SUB){ assert(st<=l && l<=st+n); //prefix(l-1)+x以下となる最大prefixをBIT上で二分探索する T base=prefix(l-1),th=op(base,x); auto [p,pref]=maxPrefix([&](ll,const T &v){ return !(th pair BIT::ceilGroup(ll l,T x)const requires(HAS_SUB){ assert(st<=l && l<=st+n); if(x==e) return {(Int)(l-1),e}; //x=0なら空区間r=l-1が最小の答 //閾値prefix(l-1)+x未満となる最大prefixを二分探索で得る //その次の要素(=p)が初めて閾値以上になる位置 T base=prefix(l-1),th=op(base,x); auto [p,pref]=maxPrefix([&](ll,const T &v){ return v pair BIT::floorGroupRev(ll r,T x)const requires(HAS_SUB){ assert(st-1<=r && r<=st+n-1); //sum[l,r]=prefix(r)-prefix(l-1)なのでprefix(l-1)>=prefix(r)-xを探す T total=prefix(r),th=sub(total,x); ll l=st+maxPrefix([&](ll,const T &v){ return v pair BIT::ceilGroupRev(ll r,T x)const requires(HAS_SUB){ assert(st-1<=r && r<=st+n-1); if(x==e) return {(Int)(r+1),e}; //x=0なら、空区間l=r+1が最大の答 T total=prefix(r); //sum[l,r]=prefix(r)-prefix(l-1)なのでprefix(l-1)<=prefix(r)-xを探す T th=sub(total,x); auto [l,pref]=maxPrefix([&](ll,const T &v){ return !(th=0、全Ai>=0が前提 auto [r,rem]=bit.floorGroup(l,x); //Al+A_{l+1}+…+Ar+rem=xなる最大のr auto [r,rem]=bit. ceilGroup(l,x); //Al+A_{l+1}+…+Ar=x+remなる最小のr ない時En()+1 auto [l,rem]=bit.floorGroupRev(r,x); //Ar+A_{r-1}+…+Al+rem=xなる最小のl auto [l,rem]=bit. ceilGroupRev(r,x); //Ar+A_{r-1}+…+Al=x+remなる最大のl ない時St()-1 */ /***************************/ /****** 多重集合用API ******/ /***************************/ namespace BITSpace{ //---- 多重集合用API: A[x]を値xの個数として扱う template void BIT::insert(ll x) requires(HAS_SUB){ apply(x,T(1)); } template bool BIT::erase(ll x) requires(HAS_SUB){ // 負の個数にすると累積値の単調性が崩れるため、削除前に確認する。 if(!contains(x)) return false; apply(x,sub(e,T(1))); return true; } template bool BIT::contains(ll x)const requires(HAS_SUB){ return get(x)>e; } template Int BIT::leftnumof(ll x)const requires(HAS_SUB){ return (Int)prefix(x); } template Int BIT::rightnumof(ll x)const requires(HAS_SUB){ return (Int)get(x,st+n-1); } // 自分より手前に並ぶ個数に1を足す。 template Int BIT::rank(ll x)const requires(HAS_SUB){ return (Int)(prefix(x-1)+1); } template Int BIT::rrank(ll x)const requires(HAS_SUB){ return (Int)(sub(total(),prefix(x))+1); } template Int BIT::nthsmall(ll r)const requires(HAS_SUB){ assert(r>=1); // 累積個数がr未満の最大prefixの直後が、r個目の位置。 return (Int)(st+maxPrefix([&](ll,const T &v){ return v Int BIT::nthbig(ll r)const requires(HAS_SUB){ assert(r>=1); T cnt=total(); // 有効な順位だけを、昇順での順位に変換する。 if(T(r)>cnt) return (Int)(st-1); return nthsmall(sub(cnt,T(r))+1); } } /* // BIT本体を上に貼り付けること - ---- 多重集合専用 ---- 加減算・単位元0・全Aiが非負整数の個数前提。すべてO(logN) // 値xは[St(),En()]。insertは範囲内のみ。範囲外の値の個数は0。 ll nm=bit.total(); //集合の要素数(重複含む) bit.insert(x); //集合にxを1つ挿入 bool b=bit.erase(x); //xを1つ削除。存在しなければ変更せずfalse bool b=bit.contains(x); //true:x∈集合 ll r=bit.rank(x); //xの小さい方からの順位 1-origin xがなければ挿入した場合の順位 ll r=bit.rrank(x); //xの大きい方からの順位 1-origin xがなければ挿入した場合の順位 ll nm=bit.leftnumof(x); //x以下の個数 ll nm=bit.rightnumof(x); //x以上の個数 ll nm=bit[x]; //xの個数 ll x=bit.nthsmall(r); //r番目に小さい要素 1-origin。ない時En()+1 ll x=bit.nthbig(r); //r番目に大きい要素 1-origin。ない時St()-1 */ /***************************/ /****** 動的imos用API ******/ /***************************/ namespace BITSpace{ //---- 動的imos用API: A[i]を差分として扱う template void BIT::rangeadd(ll l,ll r,const T &x) requires(HAS_SUB){ l=max(l,st),r=min(r,st+n-1); if(l>r) return; // 左端で加算を開始し、右端の次で打ち消す。末尾の外への更新は不要。 apply(l,x); if(r T BIT::imos(ll i)const{ return prefix(i); } } /* // BIT本体を上に貼り付けること - ---- 動的imos専用 ---- 加減算・単位元0前提。すべてO(logN) // BITには差分配列を保持。init(ini)にも差分配列を渡す。 bit.rangeadd(l,r,x); //区間[l,r]に+x T x=bit.imos(i); //第i要素の値(差分の累積和) */ /***************************/ /******** 01列用API ********/ /***************************/ namespace BITSpace{ //---- 01列用API template Int BIT::count(ll b,ll l,ll r)const requires(HAS_SUB){ assert(b==0 || b==1); l=max(l,st),r=min(r,st+n-1); if(l>r) return 0; ll ones=(ll)get(l,r); return (Int)(b ? ones : r-l+1-ones); } template Int BIT::ceilIdxOf(ll b,ll i,ll d)const requires(HAS_SUB){ assert(in(i) && d>=1); // iより前の個数を足し、列全体での順位に直す。 if(d>count(b,i,st+n-1)) return (Int)(st+n); return (Int)select01(b,(ll)count(b,st,i-1)+d); } template Int BIT::floorIdxOf(ll b,ll i,ll d)const requires(HAS_SUB){ assert(in(i) && d>=1); // i以下の最後のbから、d-1個だけ左へ戻った順位を探す。 ll total=count(b,st,i); return (Int)(d>total ? st-1 : select01(b,total-d+1)); } template void BIT::flip(ll i) requires(HAS_SUB){ assert(in(i)); set(i,1-get(i)); } template pair BIT::runRange(ll i)const requires(HAS_SUB){ assert(in(i)); // 反対の値が現れる位置に挟まれた区間がランになる。 ll b=1-(ll)get(i); return {floorIdxOf(b,i)+1,ceilIdxOf(b,i)-1}; } // 全要素の一致は、反対の値が存在しないことと同じ。 template bool BIT::isAll(ll b,ll l,ll r)const requires(HAS_SUB){ return count(1-b,l,r)==0; } template bool BIT::isAny(ll b,ll l,ll r)const requires(HAS_SUB){ return count(b,l,r)!=0; } // 左からk個目のbの位置。prefix内の0の個数は長さ-1の個数。 template ll BIT::select01(ll b,ll k)const{ return st+maxPrefix([&](ll p,const T &v){ return (b ? (ll)v : p-(ll)v)