ABC320 G - Slot Strategy 2 (Hard)
問題概要
$N$ 個の長さ $M$ の数字のリールからなるスロットがあり、毎秒一つ以下のリールが止められる。
リールが示す数字をすべて等しくするためには最短何秒かかるか。
・$N \le 100$
・$M \le 10^5$
600点
解説
この問題は、数字ごとに分けて解くことができる。文字種数を $\sigma$ とする。また、解の上界を考えると、示す数字をすべて等しくすることができるならば $NM$ 秒以下でできることが分かる。
これにより、数字 $k$ と正整数 $T (\le NM)$ に対して次のような問題が与えられる(線形計画緩和を用いた)。
$N$ 行 $T$ 列の行列 $v$ が与えられる。ただし、
$$ v_{i,j}= \begin{cases} 1 & (S_{i,j\%M}=k) \\ 0 & (S_{i,j\%M}\neq k) \end{cases} $$
である。
・$\sum_{i}x_{i, j} \le 1$
・$\sum_{j}x_{i, j} \le 1$
・$0 \le x_{i, j}$
$$ v_{i,j}= \begin{cases} 1 & (S_{i,j\%M}=k) \\ 0 & (S_{i,j\%M}\neq k) \end{cases} $$
である。
・$\sum_{i}x_{i, j} \le 1$
・$\sum_{j}x_{i, j} \le 1$
・$0 \le x_{i, j}$
これらを満たす $N$ 行 $T$ 列の行列 $x$ に対して、 $\sum_{i}\sum_{j}x_{i, j}v_{i, j}$ を最大化せよ。
この線形計画問題の答えが $N$ ならば $T$ 秒以下で示す数字をすべて $k$ にすることができることが分かる。また、 $T$ に対して答えに単調性があるので、二分探索をすることで解が分かる。
この問題は次のようなグラフを考えることで、最大流問題として解くことができる。
例えば、$N=3, M=3, T=NM=9, v = \begin{pmatrix}0 & 0 & 1 \\ 0 & 1 & 1 \\ 0 & 1 & 0\end{pmatrix}$ を考える。
しかし、これだと頂点数 $O(NM)$ 、辺数 $O(NM)$ になってしまう。頂点数 $V$ 辺数 $E$ の辺容量がすべて $1$ のグラフの最大流はDinic 法を用いることで $O(\sqrt{V}E)$ で計算できるが、今の条件ではかなり実行時間が厳しい。
なぜならば、赤色の頂点を使う代わりに、他の頂点が必ず使え、かつ解が変わらないからである。また、中間にある頂点は入次数と出次数がそれぞれ常に $1$ であり、縮約することができる。
したがって頂点数 $O(N^2)$ 、辺数 $O(N^2)$ になり、また解の候補が $O(N^2)$ 通りになるので合わせて $O(\sigma N^3 \log{N})$ で計算できることが分かる。
全体の計算量は $O(NM + \sigma N^3 \log{N})$ である。
実装上の注意
Dinic 法のライブラリを用いる。AtCoder Library やLuzhiled’s Library などがある。
AC コード
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (ll i=0; i<(ll)(n); i++)
const ll mod = 998244353;
const int inf = numeric_limits<int>::max()/2;
const ll INF = numeric_limits<ll>::max()/2;
#include <atcoder/maxflow>
void solve() {
int n, m; cin >> n >> m;
int sigma = 10;
vector<string> s(n);
rep(i, n) cin >> s[i];
vector pos(sigma, vector<vector<int>>(n));
rep(i, n) rep(j, m) if ((int)pos[s[i][j]-'0'][i].size() < n) pos[s[i][j]-'0'][i].emplace_back(j);
vector<int> unable(sigma);
rep(i, sigma) rep(j, n) {
int k = 0;
if ((int)pos[i][j].size() == 0) {
unable[i] = 1;
break;
}
while ((int)pos[i][j].size() < n) {
pos[i][j].emplace_back(pos[i][j][k] + m);
k++;
}
}
int ans = inf;
rep(k, sigma) {
if (unable[k]) continue;
vector press(n, vector<int>(n));
vector<int> st;
rep(i, n) rep(j, n) st.emplace_back(pos[k][i][j]);
sort(st.begin(), st.end());
rep(i, n) rep(j, n) press[i][j] = lower_bound(st.begin(), st.end(), pos[k][i][j]) - st.begin();
int l=-1, r = (int)st.size();
while (1LL < r - l) {
int mid = (l + r) / 2;
atcoder::mf_graph<int> g(n + n * n + 2);
int S = n + n * n, G = n + n * n + 1;
rep(i, n) g.add_edge(S, i, 1);
rep(i, n) rep(j, n) if (pos[k][i][j] <= st[mid]) g.add_edge(i, n+press[i][j], 1);
rep(i, n*n) g.add_edge(n+i, G, 1);
if (g.flow(S, G) == n) r = mid;
else l = mid;
}
if (r < (int)st.size()) ans = min(ans, st[r]);
}
cout << (ans == inf ? -1 : ans) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
コメント
コメントを投稿