AtCoder abc304 参加メモ

Tokio Marine & Nichido Fire Insurance Programming Contest 2023(AtCoder Beginner Contest 304) - AtCoder

B - Subscribers

問題文の通りにやる。 条件がたくさんあるが、よく見ると桁が増えて行く以外は同じ処理だとわかるのでうまくループ処理にするといい。n から x の位以下の数字の切り捨てる処理は、 n / (10x) * (10x) で可能

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  int now = 1e3, x = 1;
  while(1) {
    if (n < now) { cout << n/x*x; return 0; }
    now *= 10; x *= 10;
  }
  return 0;
}

C - Virus

  • ユークリッド距離は平方根を取らずに整数で扱うようにする
  • 初期に感染している人 1 から隣接行列のグラフで BFS して新規感染者がいなくなったら終了する
#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,d; cin >> n >> d;
  d *= d;
  vector<int> x(n),y(n);
  REP(i,n) cin >> x[i] >> y[i];

  vector dist(n,vector<int>(n)); // dist[i][j] := i から j の距離。前計算しておく
  REP(i,n) REP(j,n) dist[i][j] = pow(x[i]-x[j],2) + pow(y[i]-y[j],2);

  vector<bool> ans(n); // 感染していたら true
  ans[0] = true;
  queue<int> q;
  q.push(0);
  while(q.size()) {
    int v = q.front(); q.pop();
    REP(i,n) {
      if (ans[i]) continue;
      if (dist[v][i] > d) continue;
      q.push(i);
      ans[i]=true;
    }
  }

  REP(i,n) cout << (ans[i] ? "Yes" : "No") << endl;
  return 0;
}

D - A Piece of Cake

それぞれのピースにイチゴが何個乗っているか?を高速に求めたい。 n 個のイチゴがどのピースに乗っているかは a, b を二分探索することで logA + logB で求められる。そしたら最大、最小が答えとなるが、イチゴが乗っているピースが (A+1)(B+1) 以下である場合は最小が0(イチゴが1つも乗っていないピースが存在する)となるので注意する。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;
using P = pair<int,int>;

int main() {
  int w,h,n; cin >> w >> h >> n;
  vector<P> s(n);
  REP(i,n) {
    int p,q; cin >> p >> q;
    s[i] = {p,q};
  }
  int x; cin >> x; vector<int> a(x);
  REP(i,x) cin >> a[i];
  int y; cin >> y; vector<int> b(y);
  REP(i,y) cin >> b[i];

  auto f = [&](int i) -> P {
    auto [nx,ny] = s[i];
    int aid = upper_bound(a.begin(),a.end(),nx) - a.begin() - 1;
    int bid = upper_bound(b.begin(),b.end(),ny) - b.begin() - 1;
    return P(aid,bid);
  };

  map<P,int> mp; // どのピースにイチゴが何個乗っているか
  REP(i,n) mp[f(i)]++;
  vector<int> cnt;
  for(auto [_,v]: mp) cnt.push_back(v);

  ll tot = (ll)(x+1)*(y+1);
  int mn = ((ll)mp.size() < tot) ? 0 : *min_element(cnt.begin(),cnt.end());
  int mx = *max_element(cnt.begin(),cnt.end());

  printf("%d %d\n", mn, mx);
  return 0;
}

E - Good Graph

DSU で k 個の良いグラフでなくなるケースを、連結成分の代表元のペアの集合で持っておく。

Q 個の質問で、新たに結合する頂点の代表元のペアが上記の集合に存在した場合は良いグラフでなくなるため No、なければ Yes

#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using P = pair<int,int>;

int main() {
  int n,m; cin >> n >> m;
  dsu uf(n);
  REP(i,m) {
    int u,v; cin >> u >> v;
    u--; v--;
    uf.merge(u,v);
  }

  set<P> st;
  int k; cin >> k;
  REP(i,k) {
    int x,y; cin >> x >> y;
    x--; y--;
    int l1 = uf.leader(x), l2 = uf.leader(y);
    if (l1 > l2) swap(l1,l2);
    st.emplace(l1,l2);
  }

  int q; cin >> q;
  REP(i,q) {
    int u,v; cin >> u >> v;
    u--; v--;
    int l1 = uf.leader(u), l2 = uf.leader(v);
    if (l1 > l2) swap(l1,l2);
    cout << (st.count({l1,l2}) ? "No" : "Yes") << endl;
  }
  return 0;
}

AtCoder abc303 参加メモ

NS Solutions Corporation Programming Contest 2023(AtCoder Beginner Contest 303) - AtCoder

B - Discord

隣接行列で 人 i が 人 j と前後で並んでいたか?を持っておいて、一度も並んでいない二人組の数を集計する

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,m; cin >> n >> m;
  vector a(m, vector<int>(n));
  REP(i,m) REP(j,n) { cin >> a[i][j]; a[i][j]--; }

  vector s(n, vector<bool>(n));
  REP(i,m) REP(j,n) {
    int now = a[i][j];
    int prev = a[i][max(0,j-1)];
    int nx = a[i][min(n-1,j+1)];
    s[now][prev] = s[now][nx] = true;
  }

  int ans = 0;
  REP(i,n) for(int j = i+1; j < n; j++) if (!s[i][j]) ans++;
  cout << ans << endl;
  return 0;
}

C - Dash

そのままシュミレーションする。

今いる地点に回復アイテムがあるかの判定は set を使うと楽。 回復アイテムは複数回使うことはできないので、使ったら set から削除しておく。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using P = pair<int,int>;

map<char,P> mp {
  {'R', {1,0}},
  {'L', {-1,0}},
  {'U', {0,1}},
  {'D', {0,-1}}
};

int main() {
  int n,m,h,k; cin >> n >> m >> h >> k;
  string s; cin >> s;

  set<P> st;
  REP(i,m) {
    int x,y; cin >> x >> y;
    st.emplace(x,y);
  }

  int nx = 0, ny = 0;
  bool ok = true;
  REP(i,n) {
    auto [dx,dy] = mp[s[i]];
    nx += dx, ny += dy;
    h--;
    if (h < 0) { ok = false; break; }
    if (st.count({nx,ny}) && (h < k)) {
      h = k;
      st.erase({nx,ny});
    }
  }

  cout << (ok ? "Yes" : "No") << endl;
  return 0;
}

D - Shift vs. CapsLock

以下のように DP を定義して解いた

dp[i][j] := i 文字目までみたとき、 CapsLock の状態が j( on/off ) のときの最短の時間

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;
const ll LINF = 1e18;
template<class T> void chmin(T& a, T b) { a = min(a,b); }

int main() {
  ll x,y,z; cin >> x >> y >> z;
  string s; cin >> s;
  int n = s.size();

  vector dp(n+1, vector<ll>(2,LINF));
  dp[0][0] = 0;
  dp[0][1] = z;
  REP(i,n) {
    // CapsLock off
    if (s[i] == 'a') {
      chmin(dp[i+1][0], dp[i][0] + x);
      chmin(dp[i+1][1], dp[i][0] + z + y);
    } else {
      chmin(dp[i+1][0], dp[i][0] + y);
      chmin(dp[i+1][1], dp[i][0] + z + x);
    }
    // CapsLock on
    if (s[i] == 'a') {
      chmin(dp[i+1][1], dp[i][1] + y);
      chmin(dp[i+1][0], dp[i][1] + z + x);
    } else {
      chmin(dp[i+1][1], dp[i][1] + x);
      chmin(dp[i+1][0], dp[i][1] + z + y);
    }
  }

  ll ans = min(dp[n][0],dp[n][1]);
  cout << ans << endl;
  return 0;
}

E - A Gift From the Stars

次数が 1 の頂点と隣接している頂点は必ず星の中心となる。その星の中心と隣接している頂点を調べ、次数が 2 以上の頂点は他の星と連結しているため、さらにそこを探索する。これを候補がなくなるまで繰り返す。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  vector g(n,vector<int>());
  vector<int> deg(n);
  REP(i,n-1) {
    int u,v; cin >> u >> v;
    u--; v--;
    g[u].push_back(v);
    g[v].push_back(u);
    deg[u]++; deg[v]++;
  }

  // 頂点 i から 次数が 1 より大きい頂点を探す
  auto f = [&](int i) {
    if (i >= 0) for(auto v: g[i]) if (deg[v] > 1) return v;
    return -1;
  };

  vector<int> ans;
  queue<int> q;
  REP(i,n) if (deg[i] == 1) q.push(i);
  while(q.size()) {
    int center = f(q.front()); q.pop();
    if (center == -1) continue;
    deg[center] = 0;
    for(auto v: g[center]) {
      deg[v]--;
      if (deg[v] == 1) q.push(f(v));
    }
    ans.push_back(g[center].size());
  }

  sort(ans.begin(),ans.end());
  for(int v: ans) cout << v << " ";
  return 0;
}

AtCoder abc302 参加メモ

TOYOTA MOTOR CORPORATION Programming Contest 2023#2 (AtCoder Beginner Contest 302) - AtCoder

B - Find snuke

8方向全部の処理をそれぞれ記述すると行数が増えてバグらせやすい。

共通部分を考えて行数をなるべく少なくするように心がける。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using P = pair<int,int>;
const string snk = "snuke";
const vector<P> d = {{1,-1},{1,0},{1,1},{0,-1},{0,1},{-1,-1},{-1,0},{-1,1}};

int main() {
  int h,w; cin >> h >> w;
  vector<string> s(h);
  REP(i,h) cin >> s[i];

  auto f = [&](int i, int j, P dir) {
    auto [di,dj] = dir;
    vector<P> ans;
    REP(k,5) {
      if (i < 0 || j < 0 || i >= h || j >= w) return;
      if (s[i][j] != snk[k]) return;
      ans.emplace_back(i,j);
      i += di, j += dj;
    }
    for(auto [x,y]: ans) cout << x+1 << " " << y+1 << endl;
  };

  REP(i,h) REP(j,w) REP(k,8) f(i,j,d[k]);
  return 0;
}

C - Almost Equal

N <= 8 の制約のため、順列を全列挙(8!)できる。全列挙してそれぞれの条件を満たすかどうかを確かめればよい

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,m; cin >> n >> m;
  vector<string> s(n);
  REP(i,n) cin >> s[i];

  auto f = [&](vector<int>& b) {
    REP(i,n-1) {
      auto now = s[b[i]], nx = s[b[i+1]];
      int cnt = 0;
      REP(j,m) if (now[j] != nx[j]) cnt++;
      if (cnt > 1) return false;
    }
    return true;
  };

  vector<int> b(n);
  iota(b.begin(),b.end(),0);
  bool ok = 0;
  do { ok |= f(b); } while (next_permutation(b.begin(),b.end()));
  cout << (ok ? "Yes" : "No") << endl;
  return 0;
}

D - Impartial Gift

高橋君か青木君どちらか片方の贈り物を決め打って、もう片方を二分探索で最大になる値を求める。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

int main() {
  ll n,m,d; cin >> n >> m >> d;
  vector<ll> a(n),b(m);
  REP(i,n) cin >> a[i];
  REP(i,m) cin >> b[i];
  sort(b.begin(),b.end());

  ll ans = -1;
  REP(i,n) {
    ll now = a[i];
    int idx = upper_bound(b.begin(),b.end(),d+now) - b.begin() - 1;
    if (idx < 0) continue;
    if (abs(now-b[idx]) <= d) ans = max(ans, now+b[idx]);
  }
  cout << ans << endl;
  return 0;
}

E - Isolation

各頂点から接続している頂点を set で持って、それを増やしたり減らしたりする。

割りとそのままな実装でも制限時間に間に合う。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,q; cin >> n >> q;
  vector g(n,unordered_set<int>());
  int now = n;
  REP(i,q) {
    int t; cin >> t;
    if (t == 1) {
      int u,v; cin >> u >> v;
      u--; v--;
      g[u].insert(v); g[v].insert(u);
      if ((int)g[u].size() == 1) now--;
      if ((int)g[v].size() == 1) now--;
    } else {
      int v; cin >> v; v--;
      if (g[v].size()) {
        for(auto x: g[v]) {
          g[x].erase(v);
          if (g[x].size() == 0) now++;
        }
        g[v].clear(); now++;
      }
    }
    cout << now << endl;
  }
  return 0;
}

AtCoder abc301 参加メモ

パナソニックグループプログラミングコンテスト2023(AtCoder Beginner Contest 301) - AtCoder

B - Fill the Gaps

実際に配列に挿入する必要はなく、愚直に順番に出力していけばよい。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  vector<int> a(n);
  REP(i,n) cin >> a[i];
  REP(i,n-1) {
    cout << a[i] << " ";
    for(int j = a[i]+1; j < a[i+1]; j++) cout << j << " ";
    for(int j = a[i]-1; j > a[i+1]; j--) cout << j << " ";
  }
  cout << a.back() << endl;
  return 0;
}

C - AtCoder Cards

自由に並び替えしてよいため、出現する文字種別が一致すればどうかを調べればよい。

s, t でそれぞれ一致するために足りない文字を @ で補えるかどうか調べる。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  const string atc = "atcoder";
  string s,t; cin >> s >> t;
  set<char> st;
  map<char,int> ms,mt;
  for(auto c: s) { st.insert(c); ms[c]++; }
  for(auto c: t) { st.insert(c); mt[c]++; }

  bool ok = true;
  for(char c: st) {
    if (c == '@') continue;
    int d = ms[c] - mt[c];
    if (d == 0) continue;
    if (atc.find(c) == string::npos) ok = false;
    d > 0 ? mt['@'] -= d : ms['@'] -= d;
  }

  if (ms['@'] < 0 || mt['@'] < 0) ok = false;
  cout << (ok ? "Yes" : "No") << endl;
  return 0;
}

D - Bitmask

ビットが ? のところを貪欲に上から n 以下になるように立てていく

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

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

  int sz = s.size();
  ll ans = 0;
  REP(i,sz) if (s[i] == '1') ans += (1LL << (sz-i-1));
  REP(i,sz) {
    if (s[i] != '?') continue;
    ll now = 1LL << (sz-i-1);
    if (ans + now <= n) ans += now;
  }

  cout << (ans > n ? -1 : ans) << endl;
  return 0;
}

AtCoder abc301 参加メモ

パナソニックグループプログラミングコンテスト2023(AtCoder Beginner Contest 301) - AtCoder

https://atcoder.jp/contests/abc300/tasks/abc301_b

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  vector<int> a(n);
  REP(i,n) cin >> a[i];
  REP(i,n-1) {
    cout << a[i] << " ";
    for(int j = a[i]+1; j < a[i+1]; j++) cout << j << " ";
    for(int j = a[i]-1; j > a[i+1]; j--) cout << j << " ";
  }
  cout << a.back() << endl;
  return 0;
}

C - AtCoder Cards

出現数文字種別をカウントして一致できるかどうか調べる

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  const string atc = "atcoder";
  string s,t; cin >> s >> t;
  map<char,int> ms,mt;
  REP(i,(int)s.size()) { ms[s[i]]++; mt[t[i]]++; }

  auto f = [&](map<char,int> ma, map<char,int> mb) {
    for(auto [k,v]: ma) {
      if (k == '@') continue;
      int d = v - mb[k];
      if (d <= 0) continue;
      if (atc.find(k) == string::npos) return false;
      if (mb['@'] < d) return false;
      mb['@'] -= d;
    }
    return true;
  };

  bool ok = f(ms,mt) && f(mt,ms);
  cout << (ok ? "Yes" : "No") << endl;
  return 0;
}

D - Bitmask

ビットが ? のところを貪欲に上から n 以下になるように立てていく

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

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

  int sz = s.size();
  ll ans = 0;
  REP(i,sz) if (s[i] == '1') ans += (1LL << (sz-i-1));
  REP(i,sz) {
    if (s[i] != '?') continue;
    ll now = 1LL << (sz-i-1);
    if (ans + now <= n) ans += now;
  }

  cout << (ans > n ? -1 : ans) << endl;
  return 0;
}

AtCoder abc300 参加メモ

ユニークビジョンプログラミングコンテスト2023 春 (AtCoder Beginner Contest 300) - AtCoder

B - Same Map in the RPG World

H,W <= 30 のため 縦方向 shift と横方向 shift を全探索できる

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int h,w; cin >> h >> w;
  vector<string> a(h),b(h);
  REP(i,h) cin >> a[i];
  REP(i,h) cin >> b[i];

  // s: 縦方向shift数, t: 横方向shit数
  auto f = [&](int s, int t) {
    REP(i,h) REP(j,w) {
      int ni = (i+s) % h;
      int nj = (j+t) % w;
      if (a[ni][nj] != b[i][j]) return false;
    }
    return true;
  };

  int ok = 0;
  REP(i,h) REP(j,w) ok |= f(i,j);
  cout << (ok ? "Yes" : "No") << endl;
  return 0;
}

C - Cross

B と同じく H,W <= 100 のためすべての座標を全探索できる

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using P = pair<int,int>;

const vector<P> m = { {1,1}, {1,-1}, {-1,1}, {-1,-1} };

int main() {
  int h,w; cin >> h >> w;
  vector<string> c(h);
  REP(i,h) cin >> c[i];
  int n = min(h,w);

  // 座標(i,j)を中心とする x印のサイズを返す
  auto f = [&](int i, int j) {
    if (c[i][j] == '.') return 0;
    int res = 0;
    for(int k = 1; k <= n; k++) {
      for(auto [vi,vj]: m) {
        int ni = i + vi * k;
        int nj = j + vj * k;
        if (ni < 0 || nj < 0 || ni >= h || nj >= w) return res;
        if (c[ni][nj] == '.') return res;
      }
      res = k;
    }
    return res;
  };

  vector<int> ans(n+1);
  REP(i,h) REP(j,w) ans[f(i,j)]++;
  REP(i,n) cout << ans[i+1] << " ";
  return 0;
}

D - AABCC

エラトステネスの篩で素数を列挙し、そこから a < b の組み合わせとなる値を全探索する。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

vector<int> eratosthenes(int n) {
  vector<int> res;
  vector<bool> is_prime(n+1,true);
  for(int i = 2; i <= n; i++) {
    if (!is_prime[i]) continue;
    for(int j = 2*i; j <= n; j+=i) is_prime[j] = false;
    res.push_back(i);
  }
  return res;
}

int main() {
  ll n; cin >> n;
  auto p = eratosthenes(sqrt(n));
  sort(p.begin(),p.end());
  int sz = p.size();

  int ans = 0;
  REP(i,sz) for(int j = i+1; j < sz; j++) {
    ll a = p[i], b = p[j];
    ll c = sqrt( n / (a*a*b) );
    if (c <= b) break;
    int idx = upper_bound(p.begin(),p.end(),c) - p.begin();
    ans += max(0, idx-j-1);
  }

  cout << ans << endl;
  return 0;
}

E - Dice Product 3

abc298 の E - Unfair Sugoroku と似ている。

n の数が 1018 と大きいので計算量が不安になるが、公式解説 にあるように 2a * 3b * 5c で表せる数の個数は大きくないため、メモ化再帰で十分間に合う

#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;
using mint = modint998244353;

int main() {
  ll n; cin >> n;
  mint p = mint(1)/5;

  map<ll,mint> mem;
  auto dfs = [&](auto self, ll now) -> mint {
    if (now >= n) return now == n ? 1 : 0;
    if (mem.count(now)) return mem[now];
    mint res;
    for(int i = 2; i <= 6; i++) res += self(self,now*i);
    res *= p;
    return mem[now] = res;
  };

  cout << dfs(dfs,1).val() << endl;
  return 0;
}

AtCoder abc274 参加メモ

キーエンスプログラミングコンテスト2022(AtCoder Beginner Contest 274) - AtCoder

B - Line Sensor

二重ループでカウント

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'

int main() {
  int h,w; cin >> h >> w;
  vector<string> c(h);
  REP(i,h) cin >> c[i];

  REP(j,w) {
    int x = 0;
    REP(i,h) { if (c[i][j] == '#') x++; }
    cout << x << " ";
  }
  cout << endl;
  return 0;
}

C - Ameba

グラフにしてdfsで親からの距離を計算

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'
const int INF = numeric_limits<int>::max();

int n;
vector<int> a(2e5);
vector<vector<int>> g(4e5+1);
vector<int> dist(4e5+1,INF);

void dfs(int i, int p) {
  for(int v: g[i]) {
    if (v == p) continue;
    dist[v] = dist[i]+1;
    dfs(v,i);
  }
  return;
}

int main() {
  cin >> n;
  REP(i,n) cin >> a[i];

  REP(i,n) {
    g[a[i]].push_back((i+1)*2);
    g[a[i]].push_back((i+1)*2+1);
  }

  dist[1] = 0;
  dfs(1,-1);

  for(int k = 1; k <= 2*n+1; k++) cout << dist[k] << endl;
  return 0;
}

D - Robot Arms 2

x方向とy方向を独立して考えるDP。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'

int main() {
  int n,x,y; cin >> n >> x >> y;
  vector<int> a(n);
  REP(i,n) cin >> a[i];

  set<int> sx,sy;
  sx.insert(a[0]);
  sy.insert(0);
  for(int i = 1; i < n; i++) {
    set<int> t,p;
    i % 2 == 0 ? swap(p,sx) : swap(p,sy);
    for(int v: p) {
      t.insert(v+a[i]);
      t.insert(v-a[i]);
    }
    i % 2 == 0 ? swap(t,sx) : swap(t,sy);
  }

  cout << (sx.count(x) && sy.count(y) ? "Yes" : "No") << endl;
  return 0;
}

E - Booster

巡回セールスマン問題の応用。(参考問題: C - 巡回セールスマン問題)

  • dp[n][i] := すでに訪れた集合が n であって、最後にいる場所が i であるときの、合計コストの最小値
  • 宝箱も巡回する場所の一部として一緒に考えて良い。
#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'
using ll = long long;
const ll LINF = numeric_limits<ll>::max();

int main() {
  int n,m; cin >> n >> m;
  n+=1; // 原点分を追加
  int w = n+m;
  vector<int> x(w),y(w);
  for(int i = 1; i < w; i++) cin >> x[i] >> y[i];
  vector dist(w,vector<double>(w)); // dist[i][j] := iからjへの距離
  REP(i,w) REP(j,w) dist[i][j] = sqrt(powl(x[i]-x[j],2) + powl(y[i]-y[j],2));

  // dp[n][i] := すでに訪れた集合が n であって、最後にいる場所が i であるときの、合計コストの最小値
  vector dp(1<<w, vector<double>(w,LINF));
  dp[0][0] = 0;

  REP(bit, 1<<w) {
    int cnt = 0; // bitの状態で取得している宝箱数
    REP(i,m) if (bit>>(n+i) & 1) cnt++;

    // 各bitの状態でiからjへの遷移を考える
    REP(i,w) {
      if (dp[bit][i] == LINF) continue;
      REP(j,w) {
        if (bit >> j & 1) continue;
        int nb = bit | 1<<j;
        dp[nb][j] = min(dp[nb][j], dp[bit][i] + dist[i][j]/(1<<cnt));
      }
    }
  }

  double ans = LINF;
  int t = (1<<n)-1;
  REP(bit,1<<w) if ((bit&t)==t) ans = min(ans,dp[bit][0]);
  printf("%.10f\n",ans);
  return 0;
}