$\newcommand{\O}{\mathrm{O}}$
二部グラフにおいて左側の $1$ つの頂点と右側の(添字の意味で)連続する範囲内の頂点集合との間に張られた辺集合のことを "区間辺" ということにする.
入力の二部グラフの左側および右側の頂点数が $\O(n)$, その間を結ぶ区間辺の本数が $\O(m)$ である場合に最大マッチングを $\O(m \sqrt{n} \log n)$ で求めるアルゴリズム.
セグメント木を用いて区間辺を処理する手法では $\O(m n \log n)$ より良い最悪計算量を得ることが難しそうであり(?)また実測もおそらくこちらのほうが速いと思う.
(関数)
add_edge($from$, $l$, $r$): 右側頂点の $from$ から左側頂点の添字範囲 $[l, r)$ に対して容量 $1$ の辺を張る.
時間計算量: $\O(m \sqrt{n} \log n)$
// 左側の各頂点から出る区間辺は disjoint でなくても正しく動く class BM { private: struct edge { int l, r; edge(const int _l, const int _r) : l(_l), r(_r){} }; const int U, V; vector<vector<edge> > G; vector<set<int> > layer; vector<int> level, que, prv, rasg; set<int> st, pst; int bfs(){ int last = -1, qh = 0, qt = 0; for(int i = 0; i < U + V; ++i){ layer[i].clear(), level[i] = -1; } for(int i = 0; i < U; ++i){ if(asg[i] < 0) level[i] = 0, que[qt++] = i, prv[i] = -1; } st = pst; while(qh < qt){ const int u = que[qh++]; if(u >= U){ const int v = rasg[u - U]; if(v >= 0) level[v] = level[u] + 1, que[qt++] = v, prv[v] = u; else last = u; }else{ for(const edge& e : G[u]){ for(auto it = st.lower_bound(e.l); it != st.end() && *it < e.r;){ const int v = *it; layer[level[u]].insert(v), level[U + v] = level[u] + 1; que[qt++] = U + v, prv[U + v] = u, it = st.erase(it); } } } } return last; } bool dfs(const int u){ if(u >= U){ if(rasg[u - U] < 0) return true; else return dfs(rasg[u - U]); }else{ auto& vers = layer[level[u]]; for(const edge& e : G[u]){ for(auto it = vers.lower_bound(e.l); it != vers.end() && *it < e.r;){ const int v = *it; it = vers.erase(it); if(dfs(U + v)){ asg[u] = v, rasg[v] = u; return true; } } } } return false; } void hint_search(int cur, int& flow){ ++flow; while(cur >= 0){ level[cur] = -1; if(cur >= U){ layer[level[prv[cur]]].erase(cur - U); asg[prv[cur]] = cur - U, rasg[cur - U] = prv[cur]; } cur = prv[cur]; } } public: BM(const int u, const int v) : U(u), V(v), G(U + V), layer(U + V), level(U + V), que(U + V), prv(U + V), rasg(V, -1), asg(U, -1){ for(int i = 0; i < V; ++i) pst.insert(i); } void add_edge(const int from, const int l, const int r){ G[from].emplace_back(l, r); } // asg に左側頂点がどの右側頂点とマッチングされるかが格納される vector<int> asg; int solve(){ int flow = 0; for(;;){ const int cur = bfs(); if(cur < 0) break; hint_search(cur, flow); for(int i = 0; i < U; ++i){ if(asg[i] < 0) flow += dfs(i); } } return flow; } };
手元でかなりテストはした.