yukicoder No.325 マンハッタン距離2
解法
落ち着いて場合分けをします。
頂点の数え方としては,
(第一象限の点の数) + (第二象限の点の数) + (第三象限の点の数) + (第四象限の点の数) - (x 軸の点の数) - (y 軸の点の数) + (原点の点の数)
という数え方をします。
下のソースコードでは 象限の点の数は calc 関数で, 軸の点の数は calc2 関数でやっています。
calc 関数は, 原点 (0, 0) から頂点 (x, y) までの長方形で, マンハッタン距離が d 以下の頂点数がいくつあるかを調べます。
これは, d の値で適当に場合分けすればいけます。
calc 関数を使って第一象限の(x1, y1) から (x2, y2) の長方形内の頂点数を求めることを考えます。
まず, x2 >= 0, y2 >= 0 でないとダメですね。で, dx = max(0, x1), dy = max(0, y1) とすると, calc 関数で「原点(0, 0) から」と書かれているところで, 実際には左下の頂点が どの座標かがわかります。つまり (dx, dy) から (x2, y2) への頂点数を求めれば良いわけです。
左下の頂点が (dx, dy) になったので, 原点が左下として考えるときは, 距離 d を d-dx-dy にしないといけません。これに注意して, calc 関数を使います。
第二象限以降も同じです。
ull calc(ll x, ll y, ll d) { ull ret = 0; if (d <= y) { if (d <= x) { ret = (d+2)*(d+1)/2; } else { ret = (d+d-x+2)*(x+1)/2; } } else if (d-y <= x) { ret = (y+1)*(d-y+1); if (d >= x) { ret += (y + d-x+1) * (x-d+y) / 2; } else { ret += y*(y+1)/2; } } else { ret += (y+1)*(x+1); } return ret; } ull calc2(ll low, ll high, ll d) { ll mini = max(low, -d); ll maxi = min(d, high); return maxi-mini+1; } int main() { cin.tie(0); ios::sync_with_stdio(false); ll x1, y1, x2, y2, d; cin >> x1 >> y1 >> x2 >> y2 >> d; ull ans = 0; if (y2 >= 0 && x2 >= 0) { ll dx = max<ll>(0, x1); ll dy = max<ll>(0, y1); if (d-dx-dy >= 0) ans += calc(x2-dx, y2-dy, d-dx-dy); } //cout << ans << endl; if (y2 >= 0 && x1 <= 0) { ll dx = max<ll>(0, -x2); ll dy = max<ll>(0, y1); if (d-dx-dy >= 0) ans += calc(-x1-dx, y2-dy, d-dx-dy); } //cout << ans << endl; if (y1 <= 0 && x1 <= 0) { ll dx = max<ll>(0, -x2); ll dy = max<ll>(0, -y2); if (d-dx-dy >= 0) ans += calc(-x1-dx, -y1-dy, d-dx-dy); } //cout << ans << endl; if (y1 <= 0 && x2 >= 0) { ll dx = max<ll>(0, x1); ll dy = max<ll>(0, -y2); if (d-dx-dy >= 0) ans += calc(x2-dx, -y1-dy, d-dx-dy); } //cout << ans << endl; if (x1 <= 0 && 0 <= x2) ans -= calc2(y1, y2, d); //cout << ans << endl; if (y1 <= 0 && 0 <= y2) ans -= calc2(x1, x2, d); //cout << ans << endl; if (x1 <= 0 && 0 <= x2 && y1 <= 0 && 0 <= y2) ans--; cout << ans << endl; return 0; }