HDU_6105 Factory ( 最近公共祖先, 倍增法 )

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44510468/article/details/102636591

题目连接 vj or hdu

题意

给定 T 组样例,
第一行 n 个城市, m 个子公司
接下来的 n-1 行 是城市的边,
再接下来的 m 行, 是子公司按编号 1 到 m 的顺序, g 个工作室, 随后是这 g 个工作室所在城市编号
Q 组询问,
u 子公司, 到 v 子公司 的最短距离

题解

倍增求lca, 在 dfs 更新父节点, 深度 的同时更新距离
对于每一组询问, 枚举一下两个子公司所在的所有城市, 维护最小值.
嗯, 10s 的时间给了我暴力枚举的勇气

貌似还可以 Tarjan 离线法来做, 暂时还没接触过, 等掌握了在写一遍这个题吧

代码

类封装版本

#include <bits/stdc++.h> 
using namespace std; 
#define rg register 
#define sc scanf
#define pf printf
typedef long long ll; 



class lca_Doubling{ 
public:
    static const int MAXDepth = 20;
    static const int MAXN = 1e5+10;
    static const int MAXM = 3e5+10; // 有向图双倍加边
    struct EDGE {
        int v, w, to;
    }e[MAXM];
    int head[MAXM], cnt = 0; int dis[MAXN];/*dis */
    inline void add ( int u, int v, int w ) {
        e[cnt].v = v, e[cnt].w = w, e[cnt].to = head[u]; head[u] = cnt++;
    }
    int up[MAXN][MAXDepth], deep[MAXN]; 
    int n;
    lca_Doubling( ) { };
    lca_Doubling( int _n ) : n(_n) { };
    void ini_graph ( ) {
        cnt = 0; 
        memset ( head, -1, sizeof ( head ) ); 
/*dis */memset ( dis, 0, sizeof ( dis ) );
		memset ( up, 0, sizeof ( up ) );
		memset ( deep, 0, sizeof ( deep ) );
    }
    inline void ini ( int n ) { 
        this->n = n;
        dfs( 1 ); // 开始搜索
        up_ini( n ); // 开始预处理
    }
private:
    void dfs ( int u ) { // 搜索一遍, 记录所有节点的 深度, 距离, 父节点
        for ( int k = head[u]; k != -1; k = e[k].to ) {
            int v = e[k].v;
            if ( v==up[u][0] ) continue;
            deep[v] = deep[u]+1;
/*dis */    dis[v] = dis[u]+e[k].w;
            up[v][0] = u;
            dfs( v );
        }
    }
    void up_ini ( int n ) { // 预处理倍增数组
        for ( int j = 1; (1<<j) <= n; ++j )
            for ( int i = 1; i <= n; ++i )
                up[i][j] = up[up[i][j-1]][j-1];
    }
public:
    int getAncestor ( int u, int v ) { // 查找最近公共祖先
        if ( deep[u]<deep[v] ) swap( u, v );
        int d = deep[u]-deep[v];
        for ( int i = 0; i < MAXDepth; ++i )
            if ( (1<<i)&d )
                u = up[u][i];
        if ( u == v ) return u;
        for ( int i = MAXDepth-1; i >= 0; --i ) {
            if ( up[u][i] != up[v][i] ) {
                u = up[u][i]; v = up[v][i];
            }
        }
        return up[u][0];
    }
    inline int getDistance ( int u, int v ) { 
        return dis[u]+dis[v]-2*dis[getAncestor(u,v)];
    }
}lca;

vector<int> c[lca.MAXN];

int main ( ) {

    int T, 
        n, m, 
        u, v, w, 
        g, loc,
        ans;

	sc ( "%d", &T );
	while ( T-- ) {

        lca.ini_graph( );

		sc ( "%d%d", &n, &m ); 

		for ( int i = 1; i < n; ++i ) {
			sc ( "%d%d%d", &u, &v, &w );
			lca.add ( u, v, w );
			lca.add ( v, u, w );
		}

        for ( int i = 0; i < lca.MAXN; ++i ) c[i].clear ( ); // 初始化子公司的城市位置

		for ( int i = 1; i <= m; ++i ) {
			sc ( "%d", &g );
			while ( g-- ) {
				sc ( "%d", &loc );
				c[i].push_back ( loc );
			}
		}


        lca.ini( n ); // 初始化 lca 

		sc ( "%d", &g );
		while ( g-- ) { 

			sc ( "%d%d", &u, &v );

			ans = 0x3f3f3f3f;
            for ( auto i : c[u] ) 
                for ( auto j : c[v] ) 
                    ans = min ( ans, lca.getDistance( i, j) );

			pf ( "%d\n", ans );
		}


	}





	return 0;
}


单纯版本

#include <bits/stdc++.h> 
using namespace std; 
#define rg register 
#define sc scanf
#define pf printf
typedef long long ll; 



const int MAXN = 1e5+10;
const int MAXM = 3e5+10; // 有向图双倍加边
struct EDGE {
	int v, w, to;
}e[MAXM];
int head[MAXM], cnt = 0, dis[MAXN]; 
inline void add ( int u, int v, int w ) {
	e[cnt].v = v, e[cnt].w = w, e[cnt].to = head[u]; head[u] = cnt++;
}

const int MAXDepth = 20;
int up[MAXN][MAXDepth], deep[MAXN]; 

void dfs ( int u ) { // 搜索一遍, 记录所有节点的 深度, 距离, 父节点
	for ( int k = head[u]; k != -1; k = e[k].to ) {
		int v = e[k].v;
		if ( v==up[u][0] ) continue;
		deep[v] = deep[u] + 1;
		dis[v] = dis[u]+e[k].w;
		up[v][0] = u;
		dfs( v );
	}
}
void ini ( int n ) { // 预处理倍增数组
	for ( int j = 1; (1<<j) <= n; ++j )
		for ( int i = 1; i <= n; ++i )
			up[i][j] = up[up[i][j - 1]][j - 1];
}
int lca ( int u, int v ) { 
	if ( deep[u]<deep[v] ) swap( u, v );
	int d = deep[u]-deep[v];
	for ( int i = 0; i < MAXDepth; ++i )
		if ( (1<<i)&d )
			u = up[u][i];
	if ( u == v ) return u;
	for ( int i = MAXDepth-1; i >= 0; --i ) {
		if ( up[u][i] != up[v][i] ) {
			u = up[u][i]; v = up[v][i];
		}
	}
	return up[u][0];
}

vector<int> c[MAXN];

int main ( ) {

    int T, 
        n, m, 
        u, v, w, 
        g, loc,
        ans;

	sc ( "%d", &T );
	while ( T-- ) {

        cnt = 0; 
		memset ( head, -1, sizeof ( head ) ); 
		memset ( dis, 0, sizeof ( dis ) );
		memset ( up, 0, sizeof ( up ) );
		memset ( deep, 0, sizeof ( deep ) );
		for ( int i = 0; i < MAXN; ++i ) c[i].clear ( );

		sc ( "%d%d", &n, &m );
		for ( int i = 1; i < n; ++i ) {
			sc ( "%d%d%d", &u, &v, &w );
			add ( u, v, w );
			add ( v, u, w );
		}

		dfs( 1 );

		ini ( n );

		for ( int i = 1; i <= m; ++i ) {
			sc ( "%d", &g );
			while ( g-- ) {
				sc ( "%d", &loc );
				c[i].push_back ( loc );
			}
		}

		sc ( "%d", &g );
		while ( g-- ) { 

			sc ( "%d%d", &u, &v );

			ans = 0x3f3f3f3f;
            for ( auto i : c[u] ) 
                for ( auto j : c[v] ) 
                    ans = min ( ans, dis[i]+dis[j]-2*dis[lca(i,j)] );

			pf ( "%d\n", ans );
		}



	}



	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44510468/article/details/102636591