https://atcoder.jp/contests/acl1/tasks/acl1_a
解説
https://atcoder.jp/contests/acl1/submissions/16923085
まず、x,y座標がともに小さい街への移動と、x,y座標がともに大きい街への移動は
片方ができればもう片方もできるような移動になっている。
かつ、a->bで移動ができ、b->cで移動ができれば、a->cへの移動もできる。
つまり移動できる範囲は、グラフで考えると連結である範囲となる。
答えは連結な街の数となっている、これはUnionFindで達成できそうだ。
UnionFindで移動可能な頂点を連結する
いかに連結を行っていくかであるが、全組合せ1010通りになるので間に合わない。
ここで、2つの移動を考えると大変なので、x,y座標がともに小さい街への移動のみを考えることにする。
ある街P(px, py)に連結な街Q(qx, qy)は、qx<pxかつqy<pyであればいい。
効率的に行うには何かを固定するしかない。
x座標昇順でマージしていく
x座標昇順でソートした街を順番に評価していこう。
こうすることである街Pと連結する街は、これまでに処理した街に限られ、
かつ、これまでに処理した街の中でy座標がPよりも小さい街を取ってくればよくなる。
よってマージ処理を行っていくが、setを使って、y座標を保存しておこう。
だが、このままだと、マージ処理を行っていっても、結局全探索のような感じになる。
なので、マージ処理を行った街は1つにまとめることにしよう。
マージ処理ごとに街をまとめると、1マージで1つ街が消えるので、ならし最大N-1回で済む。
マージ後にどれを残すか
マージ後に頂点がある中で最も後のマージに使える点はどれだろうか。
マージするときに、それよりもy座標が小さい座標であればマージができる。
つまり、既にあるマージ後の街のグループの中で最もy座標が小さいものがマージの可能性がある。
なので、マージ後には最もy座標が小さいものを残しておけば問題ない。
実装はちょっと大変だが、setにはy座標だけでなく、街の添え字も乗っけておく。
int N; vector<tuple<int, int, int>> points; //--------------------------------------------------------------------------------------------------- void _main() { cin >> N; rep(i, 0, N) { int x, y; cin >> x >> y; points.push_back(make_tuple( x, y, i )); } sort(all(points)); set<pair<int, int>> rest; dsu uf(N); rep(i, 0, N) { int x, y, id; tie(x, y, id) = points[i]; while (0 < rest.size()) { auto ite = rest.lower_bound({ y, -1 }); if (ite != rest.begin()) { ite--; uf.merge(ite->second, id); y = ite->first; rest.erase(ite); } else { break; } } rest.insert({ y, id }); } rep(i, 0, N) printf("%d\n", uf.size(i)); }