ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

SnackDown 2016 - VCake

問題

縦と横が長さR,Cの長方形があり、それをいくつかの条件に基づいて切断し、指定された3つの面積(M,J,K)になるように分けろ、という問題です。
切断の条件は3つで、

  • 長方形の辺と平行に切断する

  • 一度切れ込みを入れたら、その部分は端から端まで切断する

  • 切った後に二つに分かれる部分は、どちらも整数になるように切断する
    というものです。
    これをT回繰り返します。


解法

この問題はO(log N)あたりまで計算量を落とさないといけないので、考察で殴る必要があります。
ということで考察をすると、まず一回目に例えば横と平行に切断するとしたとき、
ケーキはまず上下に分かれます。
3つに分けるには、上下に分かれたうちどちらかをもう一度切断する必要があります。が、残った片方はもう切断することはありません。
ということで、残る方を上側とすると、それは与えられた3つの大きさのうちのどれかにすでになっていなければなりません。
上側の面積が例えばMに一致するように切るとき、横の長さは一定なので、縦の長さは一意に決まってしまいます(MがCで割り切れなかったとき、条件をみたす切り方は存在しません)。
このように切断したとき、のこる下側の、縦の長さはR-M/Cとなります。
次は、残ったケーキを縦、または横に切断して、それぞれ面積がJ,およびKとなるかどうかを判定します。これも同様に調べることができます。
例えば再び横と平行に切断するとしたとき、
J、K両方がCで割り切れて、かつJ/CとK/Cの和がR-M/Cになれば、条件を満たす切断方法が存在します。
これを、必要な回数調べればよいです。
具体的には、RとCは交換可能なので2通り、最初に切り分けられた時点で決まるもの(先ほどの例でいうMです)を3つのうち1つ選ばなければならないので3とおり、最後に二回目の切断を縦できるか横できるかの2通りです。
全部で12通りほど調べて、それでも条件を満たす切断方法が存在しなければNo,すればYesになります。

以下が提出コードになります。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void prians(bool answer) {
  if(answer)
    cout << "Yes" << endl;
  else
    cout << "No" << endl;
}
ll t, r, c, mjk[3];

bool binary(ll x, ll y, ll z);

int main() {
  int i, k;
  bool ans = 0;
  cin >> t;
  while(t) {
    ans = 0;
    cin >> r >> c >> mjk[0] >> mjk[1] >> mjk[2];
    for(k = 0; k < 2; ++k) {
      for(i = 0; i < 3; ++i) {
        ans = binary(mjk[i], mjk[(i + 1) % 3],
                     mjk[(i + 2) % 3]);
        if(ans) break;
      }
      if(ans) break;
      swap(r, c);
    }
    prians(ans);
    --t;
  }
  return 0;
}

bool binary(ll x, ll y, ll z) {
  if(x % c != 0) return 0;
  ll now = r - x / c;
  if(now == 0) return 0;
  if(y % now == 0 && z % now == 0 &&
     (y / now + z / now) == c)
    return 1;
  if(y % c == 0 && z % c == 0 && (y / c + z / c) == now)
    return 1;
  return 0;
}