Solution -「洛谷 P5236」「模板」靜態仙人掌
阿新 • • 發佈:2020-07-23
\(\mathcal{Description}\)
Link.
給定一個 \(n\) 個點 \(m\) 條邊的仙人掌,\(q\) 組詢問兩點最短路。
\(n,q\le10^4\),\(m\le2\times10^4\)。
\(\mathcal{Solution}\)
提出一個環來考慮,從環上一點 \(u\) 到 \(v\),無非兩條路徑。可以按順序處理一個字首和。如圖:
令 \(sum_2\) 為結點 \(1\) 到 \(2\) 的順時針距離,\(sum_3\) 為結點 \(1\) 到 \(3\) 的順時針距離……特別地,\(sum_1\) 記錄整個環的大小。那麼環上 \(u\)
接下來建圓方樹(很多題解說建樹的細節與普通圖不一樣,其實正常建也沒有任何問題 qwq),發現一個圓點走進方點(點雙),在走到父親圓點的最短距離可以在 \(\text{Tarjan}\) 演算法中預處理出來。考慮圓方樹上的邊權:圓點到父親方點的邊權為上述最短距離,否則為 \(0\)。對於詢問 \((u,v)\),找到其 \(\text{LCA}\)(設為 \(w\)),分 \(w\) 的情況討論:
- \(w\) 是圓點,那麼 \(\text{LCA}\)
- \(w\) 是方點,此時樹上距離實際上是 \(u\) 和 \(v\) 走到 \(w\) 的父親圓點的距離之和,明顯時錯誤的——\(u\) 和 \(v\) 走進同一個點雙後,需要求一次最短距離而非直接在父親會和。所以可以倍增求到 \(u\) 走進點雙的第一個點(即方點向 \(u\) 點方向的兒子圓點)\(u'\),同理求出 \(v'\),利用處理的字首和求到 \(u'\) 到 \(v'\) 的最短距離。那麼答案就是 \(\operatorname{dist}(u,u')+\operatorname{dist}(u',v')+\operatorname{dist}(v',v)\)
複雜度 \(\mathcal O(n\log n)\)。不過這裡作者偷懶用 std::map
記錄了每個環的 \(sum\),寫得好看一點是可以在這一部分做到 \(\mathcal O(n)\)。但求走進點雙的第一個點似乎必須用帶 \(\log\) 的演算法 owo?
\(\mathcal{Code}\)
#include <map>
#include <cstdio>
#include <algorithm>
#define adj( g, u, v, c ) \
for ( int eid = g.head[u], v, c; v = g.to[eid], c = g.cst[eid], eid; eid = g.nxt[eid] )
const int MAXN = 2e4, MAXM = 4e4;
int n, m, q, snode, fa[MAXN + 5][15];
int dfc, top, dfn[MAXN + 5], low[MAXN + 5], stk[MAXN + 5];
int dep[MAXN + 5], dis[MAXN + 5], root[MAXN + 5];
std::map<int, int> sum[MAXN + 5];
struct Graph {
int ecnt, head[MAXN + 5], to[MAXM + 5], cst[MAXM + 5], nxt[MAXM + 5];
inline void link ( const int s, const int t, const int c ) {
to[++ ecnt] = t, cst[ecnt] = c, nxt[ecnt] = head[s];
head[s] = ecnt;
}
inline void add ( const int u, const int v, const int c ) {
link ( u, v, c ), link ( v, u, c );
}
} src, tre;
inline char fgc () {
static char buf[1 << 17], *p = buf, *q = buf;
return p == q && ( q = buf + fread ( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p ++;
}
inline int rint () {
int x = 0; char s = fgc ();
for ( ; s < '0' || '9' < s; s = fgc () );
for ( ; '0' <= s && s <= '9'; s = fgc () ) x = x * 10 + ( s ^ '0' );
return x;
}
inline void wint ( const int x ) {
if ( 9 < x ) wint ( x / 10 );
putchar ( x % 10 ^ '0' );
}
inline int min_ ( const int a, const int b ) { return a < b ? a : b; }
inline bool chkmin ( int& a, const int b ) { return b < a ? a = b, true : false; }
inline bool chkmax ( int& a, const int b ) { return a < b ? a = b, true : false; }
inline int cost ( const Graph& g, const int s, const int t ) {
adj ( g, s, u, c ) if ( u == t ) return c;
return -1;
}
inline int mncost ( const int id, int u, int v ) {
int t1 = sum[id][u], t2 = sum[id][v];
if ( t1 > t2 ) t1 ^= t2 ^= t1 ^= t2;
return min_ ( t2 - t1, sum[id][root[id]] - ( t2 - t1 ) );
}
inline void buildSquare ( const int rt, const int p, const int sid ) {
int beg = top; for ( ; stk[beg] ^ p; -- beg );
root[sid] = rt;
// loop: rt - stk[beg] - stk[beg + 1] - ... - stk[top] - rt.
for ( int i = beg, cur = p, pre = rt; i <= top; pre = cur, cur = stk[++ i] ) {
sum[sid][cur] = sum[sid][pre] + cost ( src, pre, cur );
}
sum[sid][rt] = sum[sid][stk[top]] + cost ( src, stk[top], rt );
int u;
do {
u = stk[top --];
tre.add ( sid, u, mncost ( sid, rt, u ) );
} while ( u ^ p );
}
inline void Tarjan ( const int u, const int fa ) {
dfn[u] = low[u] = ++ dfc, stk[++ top] = u;
adj ( src, u, v, c ) if ( v ^ fa ) {
if ( ! dfn[v] ) {
Tarjan ( v, u ), chkmin ( low[u], low[v] );
if ( low[v] >= dfn[u] ) {
tre.add ( u, ++ snode, 0 );
buildSquare ( u, v, snode );
}
} else chkmin ( low[u], dfn[v] );
}
}
inline void init ( const int u, const int f ) {
dep[u] = dep[fa[u][0] = f] + 1;
for ( int i = 1; i <= 14; ++ i ) fa[u][i] = fa[fa[u][i - 1]][i - 1];
adj ( tre, u, v, c ) if ( v ^ f ) {
dis[v] = dis[u] + c;
init ( v, u );
}
}
inline int calcLCA ( int u, int v ) {
if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
for ( int i = 14; ~ i; -- i ) if ( dep[fa[u][i]] >= dep[v] ) u = fa[u][i];
if ( u == v ) return u;
for ( int i = 14; ~ i; -- i ) if ( fa[u][i] ^ fa[v][i] ) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
inline int climb ( int u, const int tar ) {
for ( int i = 14; ~ i; -- i ) if ( dep[fa[u][i]] > dep[tar] ) u = fa[u][i];
return u;
}
int main () {
n = snode = rint (), m = rint (), q = rint ();
for ( int i = 1, u, v, w; i <= m; ++ i ) {
u = rint (), v = rint (), w = rint ();
src.add ( u, v, w );
}
Tarjan ( 1, 0 );
init ( 1, 0 );
for ( int u, v; q --; ) {
u = rint (), v = rint ();
int w = calcLCA ( u, v );
if ( w <= n ) wint ( dis[u] + dis[v] - 2 * dis[w] );
else {
int pu = climb ( u, w ), pv = climb ( v, w );
wint ( dis[u] - dis[pu] + dis[v] - dis[pv] + mncost ( w, pu, pv ) );
}
putchar ( '\n' );
}
return 0;
}