Solution -「CF 793G」Oleg and Chess
阿新 • • 發佈:2020-08-07
\(\mathcal{Description}\)
Link.
給一個 \(n\times n\) 的棋盤,其中 \(q\) 個互不重疊的子矩陣被禁止放棋。問最多能放多少個互不能攻擊的車。
\(n,q\le10^4\)。
\(\mathcal{Solution}\)
如果把問題轉化成“只允許在某些子矩陣上放棋”,就是一個很顯然的線段樹優化建圖最大流。源點連向行上的線段樹葉子,流量為 \(1\);行上的線段樹結點向父親連邊,流量為正無窮;對於每個矩陣,行在樹上分裂的 $\log $ 個區間分別向列在樹上分裂的 \(\log\) 個區間連邊,流量為區間長度之積;列上的線段樹與行上的類似。
於是思考怎樣轉變成這個問題就行了。提供一種方法:延長所有禁用矩陣的橫向邊,自然地把棋盤切割成若干矩形。用一條掃描線從左往右掃,維護陣列 \(lef_i\) 表示第 \(i\) 行目前最右邊的障礙。遇到矩陣的左邊界就拿來劃分,遇到右邊界就更新 \(lef\),\(\mathcal O(n^2)\) 即可實現。(也可以嘗試扔一個 Chtholly Tree 上去 www。)
關於邊的複雜度,一個可用矩陣所建出的邊是 \(\mathcal O(\log^2n)\) 的,每個可用矩陣必然對應上下兩條禁用矩陣邊界的延長線,而一個禁用矩陣提供的延長線是 \(\mathcal O(1)\) 的,所以共 \(\mathcal O(q)\)
複雜度 \(\mathcal O(\text{網路瘤})\)(大霧。
\(\mathcal{Code}\)
#include <queue> #include <cstdio> #include <vector> #include <iostream> #include <algorithm> typedef std::vector<std::pair<int, int> > vecpii; const int MAXN = 1e4, INF = 0x3f3f3f3f; int n, q; inline int rint () { int x = 0; char s = getchar (); for ( ; s < '0' || '9' < s; s = getchar () ); for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' ); return x; } namespace Dinic { const int MAXND = MAXN * 5, MAXED = 5e6; // 5e6 ? int ecnt = 1, S, T, head[MAXND + 5], curh[MAXND + 5], d[MAXND + 5]; struct Edge { int to, flow, nxt; } graph[MAXED * 2 + 5]; inline void link ( const int s, const int t, const int f ) { graph[++ ecnt] = { t, f, head[s] }; head[s] = ecnt; } inline void add ( const int s, const int t, const int f ) { #ifdef LOCAL_DEBUG printf ( "%d %d %d\n", s, t, f ); #endif link ( s, t, f ), link ( t, s, 0 ); } inline int DFS ( const int u, int iflow ) { if ( u == T ) return iflow; int oflow = 0; for ( int& i = curh[u], v, of; i; i = graph[i].nxt ) { if ( d[v = graph[i].to] == d[u] + 1 && graph[i].flow ) { of = DFS ( v, std::min ( iflow, graph[i].flow ) ); oflow += of, graph[i].flow -= of, graph[i ^ 1].flow += of; if ( ! ( iflow -= of ) ) break; } } if ( ! oflow ) d[u] = -1; return oflow; } inline bool BFS () { static std::queue<int> que; for ( int i = 1; i <= T; ++ i ) d[i] = -1; que.push ( S ), d[S] = 0; for ( int u; ! que.empty (); que.pop () ) { u = que.front (); for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ! ~ d[v = graph[i].to] && graph[i].flow ) { que.push ( v ), d[v] = d[u] + 1; } } } return ~ d[T]; } inline int calc () { int ret = 0; for ( ; BFS (); ret += DFS ( S, INF ) ) { for ( int i = 1; i <= T; ++ i ) { curh[i] = head[i]; } } return ret; } } // namespace Dinic. namespace SegmentTree { int sizt; inline int id ( const int l, const int r, const bool type ) { return type * sizt + ( ( l + r ) | ( l != r ) ); } inline void build ( const int l, const int r, const bool type ) { int rt = id ( l, r, type ), mid = l + r >> 1; if ( l == r ) { if ( ! type ) Dinic::add ( Dinic::S, rt, 1 ); else Dinic::add ( rt, Dinic::T, 1 ); return ; } if ( ! type ) { Dinic::add ( id ( l, mid, type ), rt, INF ); Dinic::add ( id ( mid + 1, r, type ), rt, INF ); } else { Dinic::add ( rt, id ( l, mid, type ), INF ); Dinic::add ( rt, id ( mid + 1, r, type ), INF ); } build ( l, mid, type ), build ( mid + 1, r, type ); } inline void extract ( const int l, const int r, const int el, const int er, const bool type, vecpii& vec ) { int rt = id ( l, r, type ), mid = l + r >> 1; if ( el <= l && r <= er ) return vec.push_back ( { rt, r - l + 1 } ); if ( el <= mid ) extract ( l, mid, el, er, type, vec ); if ( mid < er ) extract ( mid + 1, r, el, er, type, vec ); } inline void init () { sizt = id ( n, n, 0 ); Dinic::S = sizt * 2 + 3, Dinic::T = Dinic::S + 1; build ( 1, n, 0 ), build ( 1, n, 1 ); } inline void secLink ( const int rl, const int rr, const int cl, const int cr ) { #ifdef LOCAL_DEBUG printf ( "sl %d %d %d %d\n", rl, rr, cl, cr ); #endif static vecpii R, C; R.clear (), C.clear (); extract ( 1, n, rl, rr, 0, R ), extract ( 1, n, cl, cr, 1, C ); for ( auto r: R ) for ( auto c: C ) Dinic::add ( r.first, c.first, r.second * c.second ); } } // namespace SegmentTree. namespace Partition { int scnt, lef[MAXN + 5]; struct Segment { int r1, r2, c; bool type; inline bool operator < ( const Segment t ) const { return c ^ t.c ? c < t.c : type < t.type; } } seg[MAXN * 2 + 5]; inline void readSeg () { for ( int i = 1, r1, c1, r2, c2; i <= q; ++ i ) { r1 = rint (), c1 = rint (), r2 = rint (), c2 = rint (); seg[++ scnt] = { r1, r2, c1, 0 }, seg[++ scnt] = { r1, r2, c2, 1 }; } seg[++ scnt] = { 1, n, n + 1, 0 }, seg[++ scnt] = { 1, n, n + 1, 1 }; } inline void part () { std::sort ( seg + 1, seg + scnt + 1 ); for ( int i = 1; i <= scnt; ++ i ) { if ( seg[i].type ) { for ( int j = seg[i].r1; j <= seg[i].r2; ++ j ) { lef[j] = seg[i].c; } } else { for ( int j = seg[i].r1, lasw = seg[i].c - 1, lash = 0; j <= seg[i].r2 + 1; ++ j ) { if ( j == seg[i].r2 + 1 || lef[j] ^ lasw ) { if ( lasw + 1 < seg[i].c ) { SegmentTree::secLink ( lash, j - 1, lasw + 1, seg[i].c - 1 ); } lasw = lef[j], lash = j; } } } } } } // namespace Partition. int main () { n = rint (), q = rint (); Partition::readSeg (); SegmentTree::init (); Partition::part (); printf ( "%d\n", Dinic::calc () ); return 0; }