扩展Tarjan求解树上两点路径上的最长边(高效求解次小生成树)

SPOJ 3978 Distance Query

题意:给出一棵有边权的树(100000个点),有100000次讯问两点间路径上的最长边和最短边。

扩展Tarjan算法可以离线解决LCA问题(http://blog.csdn.net/kksleric/article/details/7442258)因此也可用于维护两点间路径上的性质。

设mx[i]为i点到当前集合根节点(tarjan过程中并查集的父亲)路径上的最长边,在合并和路径压缩时根据意义进行相应改变,那么在点a的子树都完成dfs后,所有lca是a的查询<x,y>,其路径上的最长边=MAX{mx[a],mx[b]}.

高效求解次小生成树

普通次小生成树的做法首先最小生成树上每个点为根dfs,求出树上任意两点之间的最长边。然后枚举所有不在最小生成树上的边,将其添加到树上必定形成一个环,去掉环上的最长边形成的生成树最小,由此得出(严格)次小生成树。因此复杂度为O(nlogn+n*n)。利用扩展tarjan,求出所有不在最小生成树中的边 <a,b>之间在最小生成树中路径上的最长边,相当于进行O(n) 次查询,因此复杂度为O(nlogn+n)。

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
struct node {
		int be, ne, val;
		void init(int b, int e, int v) {
			be = b;
			ne = e;
			val = v;
		}
	};
	node buf[200010], query[200010],res[100010];
	int Eb[100010], lb, Eq[100010], lq,Er[100010],lres;
	void addres(int a,int b,int v){
		res[lres].init(b,Er[a],v);
		Er[a]=lres++;
	}
	void addedge(int a, int b, int v) {
		buf[lb].init(b, Eb[a], v);
		Eb[a] = lb++;
		buf[lb].init(a, Eb[b], v);
		Eb[b] = lb++;
	}

	void addquery(int a, int b, int v) {
		query[lq].init(b, Eq[a], v);
		Eq[a] = lq++;
		query[lq].init(a, Eq[b], v);
		Eq[b] = lq++;
	}

	int f[100010], vis[100010];
	int mn[100010],mx[100010],inf=1<<28;
	int  n,ans1[100010],ans2[100010];

	int find(int x) {
		int temp=f[x];
		if (x != temp)
			f[x] = find(f[x]);
		mn[x]=min(mn[x],mn[temp]);
		mx[x]=max(mx[x],mx[temp]);
		return f[x];
	}

	void init() {
		lq = lb =lres=0;
		for (int i = 1; i <= n; i++) {
			f[i] = i;
			Er[i]=Eb[i] = Eq[i] = -1;
		    mx[i]=-inf;
		    mn[i]=inf;
		}
	}

	void dfs(int a) {
		vis[a]=1;
		for (int i = Eq[a]; i != -1; i = query[i].ne) {
			int b = query[i].be;
			if (vis[b] == 1){
				int temp=find(b);
				addres(temp,a,i);
				}
		}
		for (int i = Eb[a]; i != -1; i = buf[i].ne) {
			int b = buf[i].be;
			if (vis[b] == 0){
				dfs(b);
				f[b] =a;
			    mx[b]=mn[b]=buf[i].val;
			}
		}
		for(int i=Er[a];i!=-1;i=res[i].ne){
			int x=res[i].be;
			int t=res[i].val;
			int y=query[t].be;
			find(x);
		    find(y);
			ans1[query[t].val]=min(mn[x], mn[y]);
			ans2[query[t].val]=max(mx[x], mx[y]);
		}
	}
	int main(){
		int m,a,b,v;
		scanf("%d",&n);
		init();
		for(int i=1;i<n;i++)
		{
			scanf("%d%d%d",&a,&b,&v);
			addedge(a,b,v);
		}
		scanf("%d",&m);
		for(int i=0;i<m;i++){
			scanf("%d%d",&a,&b);
			addquery(a,b,i);
		}
		dfs(1);
		for(int i=0;i<m;i++)
			printf("%d %d\n",ans1[i],ans2[i]);
		return 0;
	}



你可能感兴趣的:(扩展Tarjan求解树上两点路径上的最长边(高效求解次小生成树))