$\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;
}
};
手元でかなりテストはした.