競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 315F

ABC315F

スキップした頂点が多いほどペナルティが大きくなる. スキップできる箇所はそんなに多くないのがポイント.
スキップしないで頂点を通った場合,最大で \(2^{1/2} 10^{4}*n \leq 1.5 \cdot 10^{8}\). ペナルティがこれよりも大きい場合,ペナルティを受けるほうが確実に損をするので調べる必要がない. \(2^{c-1} \geq 10^{8}\) となる \(c\) は調べる必要がない.

\(i \in n\) と \(j \leq min(30,i+1)\) に対して,
\(dp_{i,j} := \) \([0,i]\) まで頂点を調べて,最後に \(i\) で止まっていて, \([0,i]\) の中から \(j\) 個の頂点をスキップしたときの min distance.
とおく.
遷移を考える. 例えば,頂点\([0,3]\) までスキップするか通るか決めていて,今は頂点3に居るとする. 頂点 2 をスキップしているとする. 次に遷移するべき点は,

  • 頂点4: スキップは 2の 1つ
  • 頂点5: スキップは 2,4の 2つ
  • 頂点6: スキップは 2,4,5の 3つ

の様になる.

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

#include<bits/stdc++.h>
using namespace std;

#include<atcoder/segtree>
using namespace atcoder;


typedef long long ll;
const ll INFL = 1LL << 60;
using vll = vector<long long>;  using vvll = vector<vll>;
using pll = pair<ll,ll>;
#define srep(i,s,t) for (ll i = (s); i < (t); ++i)
#define rep(i,n) srep(i,0,(n))
#define rep1(i,n) srep(i,1,*1

#define dsrep(i,t,s) for(ll i = (t)-1; i >= (s); --i)
#define drep(i,n) dreprng(i,0,(n))
#define drep1(i,n) dreprng(i,1,(n+1))


#define fore(i,a) for(auto &i:(a))
#define forit(i,a) for(auto i = (a).begin(); i != (a).end(); i++)

#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()

template<typename T> bool in(T x, T l, T r){
  if(l >= r) return false;
  return l <= x && x < r;
}
template<typename T> bool in(T x, T a){ return in(x, (T)0, a); }
template<typename T> bool in(T x, T l, T r){
  if(l >= r) return false;
  return l <= x && x < r;
}
template<typename T> bool in(T x, T a){ return in(x, (T)0, a); }
template<typename T> bool in(T x, set<T> &s){ return s.find(x) != s.end(); }
template<typename T> bool in(T x, vector<T> &v){
  fore(i, v){ if(i == x) return true; }
  return false;
}
// @ debug
template<typename T> bool in(char x, string &v){
  fore(i, v){ if(i == x) return true; }
  return false;
}


template<class T>bool chmax(T& a, const T& b) { if (a < b) { a = b; return 1; } return 0; }
template<class T>bool chmin(T& a, const T& b) { if (a > b) { a = b; return 1; } return 0; }

#define pcntll __builtin_popcountll
#define square(a) *2
template<typename T>void uni(T& a){sort(rng(a));a.erase(unique(rng(a)),a.end());}


#define rep1(i,n) srep(i,1,*3
#define dreprng(i,l,r) for(ll i=*4
#define drep1(i,n) dreprng(i,1,(n+1))

template<typename T> void cinv(vector<T> &v) { rep(i, v.size()) cin >> v[i]; }
template<typename T> void cinvv(vector<T> &m) {
  rep(i, m.size()){
    rep(j, m[i].size()){
      cin >> m[i][j];
    }
  }
}

template<typename T> using min_priority_queue = priority_queue<T, vector<T>, greater<T>>;

void yesno(bool f){ if(f){ cout << "Yes" << endl; } else{ cout << "No" << endl; } }


int main() {
  ll n;
  cin >> n;
  vll x(n), y(n);
  rep(i,n){
    cin >> x[i] >> y[i];
  }

  auto dis = [&](ll i, ll j) -> double {
    ll dx = x[i]-x[j];
    ll dy = y[i]-y[j];
    return sqrt(dx*dx + dy*dy);
  };
 
  // [0,i] まで調べて
  // j 個スキップ
  // とくに j <= i が必要
  const ll m = 30;
  vector<vector<double>> dp(n, vector<double>(m,INFL));
  dp[0][0] = 0;
  rep(i,n) rep(j,m) {
    for(ll ni=i+1, nj=j; ni<n && nj<m; ni++, nj++){
      if(j<=i) // 無くても AC
        chmin(dp[ni][nj], dp[i][j]+dis(i,ni));
    }
  }
 
  double ans = INFL;
  rep(j,m)  chmin(ans, dp[n-1][j] + (1LL<<j>>1LL)); // j = 0 のとき,ペナルティが 0
  printf("%.10lf\n",ans);

  return 0;
}

*1:n)+1

*2:a)*(a

*3:n)+1

*4:r)-1); i>=(l); i-- )

#define drep(i,n) dreprng(i,0,(n