競技プログラミング日記

主に AtCoder の記事です

AtCoder Regular Contest 109B

 

ARC109B

 n まででなく,  1, \cdots,n, n+1  n+1 が入っているのが気になる.
まず,丸太の買い方として,同じ値段なら長いほうが得. 言い換えると,短いほうで出来るなら,長いほうでも出来る, いわば上位互換. 上位互換の考えは,貪欲法を示すときによく使う. 大きい方から買うので,  n+1, n , n-1 , \dots を買う.
買い方の次に考えるのは切り方. 買った丸太のうち,切らないでそのまま使えるものは使ったほうが良さそう. 実際,その考えで正しい.
買った丸太のうち短い方から,分割の仕方を決めていく. 長さ  y の丸太をもし切るのなら,もっと大きい丸太の中から, 長さ  y のパーツを切らないといけない. その2つを取り換えれば,長さ y の丸太を切らずに済む. これを繰り返せば,  n+1 以外の丸太は切らなくて済む.
ここまでくれば,買う本数  x を固定したときに, それで条件を満たすか(  1,\cdots, n を作れるか)は判定できる. つまり,  n+1 の丸太から,残った作りたいパーツを切り取れるかを判定すればよい.
また, 沢山丸太を買ったほうが上位互換なので,  x に関して単調なことも言えるため, binary search で解ける.
 n + (n-1) + \cdots の計算をするときに,最悪  n^{2} 程度かかる. n が  10^{18} 程度なので,long long だと桁あふれするので, boost の可変長整数を用いる.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

#include<boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
namespace mp = boost::multiprecision;
using ll = mp::cpp_int;

int main() {
  ll n;
  cin >> n;

  auto f = [&](ll x) -> bool {
    if(x >= n) return true;
    ll k = n+1 - x;
    if(k <= 0) return true;

    return (k+1)*k <= 2*(n+1);
  };
 
  ll ok, ng;
  ok = n, ng = 0;
  while(abs(ok-ng) > 1){
    ll md = (ok + ng)/2;
    if(f(md)) ok = md;
    else ng = md;
  }
  cout << ok << endl;

  return 0;
}