なんとか通した。結構迷走した。
問題概要
0 と 1 と 2 のみからなる の行列が与えられる。これらの行列のある 3 つの行が good であるとは、「どの列についても、それら 3 行の値がすべて等しいか、すべて互いに異なる」という条件を満たすことを言う。
この行列の行を 5 つ選ぶ方法であって、その 5 行の中から good な 3 行の組を 2 つ以上選べるようなものの個数を求めよ。
制約
- どの 2 つの行も互いに異なる
考えたこと
0, 1, 2 のみかななる 3 値 が、すべて等しいかすべて互いに異なるという条件は、
と言い換えられる。これはド典型らしい。このことから言えるのは、2 行を決めると、good になるべき残りの行の要素はただ一つに決まるということだ。
このことと「入力行列のどの 2 行も互いに異なる」という条件から、条件を満たす 5 行において、good な 3 行の組が 2 つとれるとき、
- 2 組の間で 1 つの行を共有している (行ベクトルを とする)
- 残りの 4 行は、2 ペアに分けられて
- 行ベクトル と から作れる行ベクトル
- 行ベクトル と から作れる行ベクトル
という形になることがわかる。 であるとき、これら 5 行は自動的にすべて互いに異なることになるので、ダブルカウントを気にする必要はない。
よって、次の方法によって の計算量で解ける。
- 行 を固定して、これが good な 2 組が共有する行とする
- 残りの各行 について
- 行 と good にある行ベクトルを計算し、それが行列に含まれるかを check する
- 含まれる個数を としたとき、 通りを加算していく
コード
#include <bits/stdc++.h> using namespace std; int main() { int N, K; cin >> N >> K; vector<vector<int>> A(N, vector<int>(K)); for (int i = 0; i < N; ++i) for (int j = 0; j < K; ++j) cin >> A[i][j]; sort(A.begin(), A.end()); long long res = 0; for (int i = 0; i < N; ++i) { long long num = 0; for (int j = 0; j < N; ++j) { if (j == i) continue; vector<int> sum(K, 0); for (int k = 0; k < K; ++k) { sum[k] = (3 - (A[i][k] + A[j][k]) % 3) % 3; } int it = lower_bound(A.begin(), A.end(), sum) - A.begin(); if (A[it] == sum) ++num; } num /= 2; res += num * (num - 1) / 2; } cout << res << endl; }