Solution -「CF 487E」Tourists
阿新 • • 發佈:2020-07-22
\(\mathcal{Description}\)
Link.
維護一個 \(n\) 個點 \(m\) 條邊的簡單無向連通圖,點有點權。\(q\) 次操作:
- 修改單點點權。
- 詢問兩點所有可能路徑上點權的最小值。
\(n,m,q\le10^5\)。
\(\mathcal{Solution}\)
怎麼可能維護圖嘛,肯定是維護圓方樹咯!
一個比較 naive 的想法是,每個方點維護其鄰接圓點的最小值,樹鏈剖分處理詢問。
不過修改的複雜度會由於菊花退化:修改”花蕊“的圓點,四周 \(\mathcal O(n)\) 個方點的資訊都需要修改。
聯想到 array 這道題,我們嘗試”弱化“方點所維護的資訊。每個方點,維護其圓方樹上兒子們的點權最小值
於是,每個方點用 std::multiset
或者常見的雙堆 trick 維護最小值資訊(推薦後者,常數較小),再用一樣的樹剖處理詢問即可。
\(\mathcal{Code}\)
#include <queue> #include <cstdio> #define adj( g, u, v ) \ for ( int eid = g.head[u], v; v = g.to[eid], eid; eid = g.nxt[eid] ) const int MAXN = 2e5, MAXM = 4e5; int n, m, q, val[MAXN + 5], snode; int dfc, tp, dfn[MAXN + 5], low[MAXN + 5], stk[MAXN + 5]; int siz[MAXN + 5], dep[MAXN + 5], fa[MAXN + 5], son[MAXN + 5]; int top[MAXN + 5]; inline bool chkmin ( int& a, const int b ) { return b < a ? a = b, true : false; } struct Graph { int ecnt, head[MAXN + 5], to[MAXM + 5], nxt[MAXM + 5]; inline void link ( const int s, const int t ) { to[++ ecnt] = t, nxt[ecnt] = head[s]; head[s] = ecnt; } inline void add ( const int u, const int v ) { link ( u, v ), link ( v, u ); } } src, tre; struct Heap { std::priority_queue<int, std::vector<int>, std::greater<int> > val, rem; inline void push ( const int ele ) { val.push ( ele ); } inline void pop ( const int ele ) { rem.push ( ele ); } inline int top () { for ( ; ! val.empty () && ! rem.empty () && val.top () == rem.top (); val.pop (), rem.pop () ); return val.empty () ? -1 : val.top (); } } heap[MAXN * 2 + 5]; struct SegmentTree { int mn[MAXN << 3]; inline void pushup ( const int rt ) { chkmin ( mn[rt] = mn[rt << 1], mn[rt << 1 | 1] ); } inline void update ( const int rt, const int l, const int r, const int x, const int v ) { if ( l == r ) return void ( mn[rt] = v ); int mid = l + r >> 1; if ( x <= mid ) update ( rt << 1, l, mid, x, v ); else update ( rt << 1 | 1, mid + 1, r, x, v ); pushup ( rt ); } inline int query ( const int rt, const int l, const int r, const int ql, const int qr ) { if ( ql <= l && r <= qr ) return mn[rt]; int ret = 2e9, mid = l + r >> 1; if ( ql <= mid ) chkmin ( ret, query ( rt << 1, l, mid, ql, qr ) ); if ( mid < qr ) chkmin ( ret, query ( rt << 1 | 1, mid + 1, r, ql, qr ) ); return ret; } } st; inline void Tarjan ( const int u, const int f ) { dfn[u] = low[u] = ++ dfc, stk[++ tp] = u; adj ( src, u, v ) if ( v ^ f ) { if ( ! dfn[v] ) { Tarjan ( v, u ), chkmin ( low[u], low[v] ); if ( low[v] >= dfn[u] ) { tre.add ( u, ++ snode ); do { tre.add ( snode, stk[tp] ); heap[snode].push ( val[stk[tp]] ); } while ( stk[tp --] ^ v ); } } else chkmin ( low[u], dfn[v] ); } } inline void DFS1 ( const int u, const int f ) { dep[u] = dep[fa[u] = f] + 1, siz[u] = 1; adj ( tre, u, v ) if ( v ^ f ) { DFS1 ( v, u ), siz[u] += siz[v]; if ( siz[v] > siz[son[u]] ) son[u] = v; } } inline void DFS2 ( const int u, const int tp ) { top[u] = tp, dfn[u] = ++ dfc; if ( son[u] ) DFS2 ( son[u], tp ); adj ( tre, u, v ) if ( v ^ fa[u] && v ^ son[u] ) DFS2 ( v, v ); } inline int queryChain ( int u, int v ) { int ret = 2e9; while ( top[u] ^ top[v] ) { if ( dep[top[u]] < dep[top[v]] ) u ^= v ^= u ^= v; chkmin ( ret, st.query ( 1, 1, snode, dfn[top[u]], dfn[u] ) ); u = fa[top[u]]; } if ( dep[u] < dep[v] ) u ^= v ^= u ^= v; chkmin ( ret, st.query ( 1, 1, snode, dfn[v], dfn[u] ) ); if ( v > n && fa[v] ) chkmin ( ret, val[fa[v]] ); return ret; } int main () { scanf ( "%d %d %d", &n, &m, &q ), snode = n; for ( int i = 1; i <= n; ++ i ) scanf ( "%d", &val[i] ); for ( int i = 1, u, v; i <= m; ++ i ) { scanf ( "%d %d", &u, &v ); src.add ( u, v ); } Tarjan ( 1, 0 ), dfc = 0; DFS1 ( 1, 0 ), DFS2 ( 1, 1 ); for ( int i = 1; i <= n; ++ i ) st.update ( 1, 1, snode, dfn[i], val[i] ); for ( int i = n + 1; i <= snode; ++ i ) st.update ( 1, 1, snode, dfn[i], heap[i].top () ); char op[5]; int a, b; for ( ; q --; ) { scanf ( "%s %d %d", op, &a, &b ); if ( op[0] == 'C' ) { st.update ( 1, 1, snode, dfn[a], b ); if ( fa[a] ) { heap[fa[a]].pop ( val[a] ); heap[fa[a]].push ( b ); st.update ( 1, 1, snode, dfn[fa[a]], heap[fa[a]].top () ); } val[a] = b; } else { printf ( "%d\n", queryChain ( a, b ) ); } } return 0; }