• Solution 「洛谷 P4320」道路相遇


    \(\mathcal{Description}\)

      Link.

      给定一个 \(n\) 个点 \(m\) 条边的连通无向图,并给出 \(q\) 个点对 \((u,v)\),询问 \(u\)\(v\) 的路径所必经的结点个数。

      \(n,q\le5\times10^5\)\(q\le\min\{\frac{n(n-1)}2,10^6\}\)

    \(\mathcal{Solution}\)

      大概是双倍经验吧。

      建出圆方树,预处理圆方树上每个点到根经过的圆点个数,然后求 LCA 计算答案即可。

      复杂度 \(\mathcal O(n\log n)-\mathcal O(\log n)\)

    \(\mathcal{Code}\)

    #include <cstdio>
    
    const int MAXN = 5e5, MAXM = 1e6;
    int n, m, q, snode;
    int dfc, top, dfn[MAXN + 5], low[MAXN + 5], stk[MAXN + 5];
    int dep[MAXN * 2 + 5], cnt[MAXN * 2], fa[MAXN * 2 + 5][20];
    
    struct Graph {
    	int ecnt, head[MAXN * 2 + 5], to[MAXM * 2 + 5], nxt[MAXM * 2 + 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;
    
    inline bool chkmin ( int& a, const int b ) { return b < a ? a = b, true : false; }
    
    inline void Tarjan ( const int u, const int f ) {
    	dfn[u] = low[u] = ++ dfc, stk[++ top] = u;
    	for ( int i = src.head[u], v; i; i = src.nxt[i] ) {
    		if ( ( v = src.to[i] ) == f ) continue;
    		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[top] ); while ( stk[top --] ^ v );
    			}
    		} else chkmin ( low[u], dfn[v] );
    	}
    }
    
    inline void init ( const int u, const int f ) {
    	dep[u] = dep[fa[u][0] = f] + 1, cnt[u] = cnt[f] + ( u <= n );
    	for ( int i = 1; i <= 19; ++ i ) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	for ( int i = tre.head[u], v; i; i = tre.nxt[i] ) {
    		if ( ( v = tre.to[i] ) ^ f ) {
    			init ( v, u );
    		}
    	}
    }
    
    inline int calcLCA ( int u, int v ) {
    	if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
    	for ( int i = 19; ~ i; -- i ) if ( dep[fa[u][i]] >= dep[v] ) u = fa[u][i];
    	if ( u == v ) return u;
    	for ( int i = 19; ~ i; -- i ) if ( fa[u][i] ^ fa[v][i] ) u = fa[u][i], v = fa[v][i];
    	return fa[u][0];
    }
    
    int main () {
    	scanf ( "%d %d", &n, &m ), snode = n;
    	for ( int i = 1, u, v; i <= m; ++ i ) {
    		scanf ( "%d %d", &u, &v );
    		src.add ( u, v );
    	}
    	Tarjan ( 1, 0 ), init ( 1, 0 );
    	scanf ( "%d", &q );
    	for ( int i = 1, u, v; i <= q; ++ i ) {
    		scanf ( "%d %d", &u, &v );
    		int w = calcLCA ( u, v );
    		printf ( "%d\n", cnt[u] + cnt[v] - cnt[w] - cnt[fa[w][0]] );
    	}
    	return 0;
    }
    
  • 相关阅读:
    驱动中回溯函数的调用关系
    CSI-MIPI学习笔记
    1920*1080分辨率和1080p,1080i的关系
    V4L2驱动内核文档翻译(一)
    signal()信号操作
    617. Merge Two Binary Trees
    Java中的集合
    Switch能否用string做参数
    Java面试题
    八种基本数据类型的大小,以及他们的封装类
  • 原文地址:https://www.cnblogs.com/rainybunny/p/13363085.html
Copyright © 2020-2023  润新知