[第一回日本最強プログラマー学生選手権-予選-] D - Classified

[Q] https://atcoder.jp/contests/jsc2019-qual/tasks/jsc2019_qual_d

エスパーだけど、貪欲的に解答。
考察

N=4のとき

(NG) BFS的に二部グラフを作ってみると K=3 で構成される
1.
1-2
|\
4 3

2.
1 2
 /|
4 3

3.
1 2

4-3

(OK) DFS的に二部グラフを作ってみると K=2 で構成される
1.
1-2
| |
4-3

2.
1 2
 X
4 3

0. DFSで辺を消費しようと思う。そしてこれがAC。
1. 全頂点への有向グラフを張る。大きい数字へ移動できるとする。
G[0] = {1,2,3,4,..,N-1}
G[1] = {2,3,4,...,N-1}
2. 頂点を赤と黒で塗分けていく。同じ色への移動は許されない。
3. 使用した辺はeraseする。
Q. eraseたくさんするなら、vectorよりlistのほうが早い?
A. vectorのほうが早い。なぜ??

N=500
vector: 31ms
list: 59ms

N=2000
vector: 875ms
list: 2785ms

vectorでeraseしたら、後ろにある要素を全部コピーしなおすから、最悪O(N)と思ってた。
listでeraseしたら、前後への移動を繋ぎ変えるだけだからO(1)と思ってた。
ところが要素2000以下ならvectorのほうが早いようだ。

実装

vector<ll> G[505];
ll N;
ll ans[505][505];
ll cnts;  // N-1C2になったらおしまい

void dfs(ll v, vector<ll> &col, ll &n) {
 for (auto it = G[v].begin(); it != G[v].end(); ++it) {
   auto u = *it;
   if (col[u] == col[v]) continue;
   col[u] = -col[v];
   ++cnts;
   ans[min(u, v)][max(u, v)] = n;
   G[v].erase(it--);
   dfs(u, col, n);
 }
}

int main() {
 cincout();

 cin >> N;
 ll fin = N * (N - 1) / 2;

 rep(i, N) {
   G[i].reserve(N - i);
   for (ll j = i + 1; j < N; ++j) {
     G[i].push_back(j);
   }
 }
 ll nb = 0;
 while (cnts < fin) {
   ++nb;
   vector<ll> col(N, 0); // 0:default +-1:color
   rep(i, N) {
     if (col[i] == 0) col[i] = 1;
     dfs(i, col, nb);
   }
 }

 rep(i, N) {
   for (ll j = i + 1; j < N; ++j) {
     cout << ans[i][j] << " \n"[j == N - 1];
   }
 }
}

https://atcoder.jp/contests/jsc2019-qual/submissions/30942687

いいなと思ったら応援しよう!