hihoCoder #1167 : 高等理论计算机科学(LCA)

Description:

少女幽香这几天正在学习高等理论计算机科学,然而她什么也没有学会,非常痛苦。所以她出去晃了一晃,做起了一些没什么意义的事情来放松自己。
门前有一颗n个节点树,幽香发现这个树上有n个小精灵。然而这些小精灵都比较害羞,只会在一条特定的路径上活动。第i个小精灵会在ai到bi的路径上活动。
两个小精灵是朋友,当且仅当它们的路径是有公共点的。
于是幽香想要知道,有多少对小精灵a和b,a和b是朋友呢?其中a不等于b,a,b和b,a看做一对。

Input:

第一行n和P (1 <= n, P <=100000),表示树的大小和小精灵的个数。树的节点从1到n标号。
接下来n-1行,每行两个数a,b,表示a到b之间有一条边。
接下来P行,第i行两个数ai,bi,表示小精灵i的活动范围是ai到bi,其中ai不等于bi。

Output:

一行答案,表示对数。

Sample Input:

6 3
1 2
2 3
2 4
4 5
4 6
1 3
1 5
5 6

Sample Output:

2

题目链接

对于小精灵的活动范围离线查询LCA,统计每个LCA节点i的LCA数量Cnt[i],之后Dfs算出根节点到节点i的LCA数量值之和Sum[i],计算每个小精灵活动范围路径上的其他小精灵活动范围LCA节点的个数(两端点a,b之间的个数就是Sum[a]+Sum[b]-2×Sum[LCA(a,b)]),最后由于某些节点不止是一条路径的LCA,需要对最终答案乘以C(2,Cnt)。

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;

struct Edge {
	int V, Next;
};

struct Query {
	int Q, Next;
	int Index;
};

struct Input {
	int U, V;
};

int N, P;
int Pre[maxn << 2];
Edge edges[maxn << 2];
int Head[maxn];
int Tot;
Query querys[maxn << 2];
int QHead[maxn];
int QTot;
int Vis[maxn];
int Ancestor[maxn];
int Answer[maxn];
int Sum[maxn];
int Cnt[maxn];
Input inputs[maxn];
long long Ans;

int Find(int X) {
	int R = X;
	while (Pre[R] != -1) {
		R = Pre[R];
	}
	return R;
}

void Join(int U, int V) {
	int RU = Find(U), RV = Find(V);
	if (RU != RV) {
		Pre[RU] = RV;
	}
}

void Init() {
	Tot = 0;
	memset(Head, -1, sizeof(Head));
	QTot = 0;
	memset(QHead, -1, sizeof(QHead));
	memset(Vis, false, sizeof(Vis));
	memset(Pre, -1, sizeof(Pre));
	memset(Ancestor, 0, sizeof(Ancestor));
	memset(Sum, 0, sizeof(Sum));
	memset(Cnt, 0, sizeof(Cnt));
}

void Addedge(int U, int V) {
	edges[Tot] = Edge {V, Head[U]};
	Head[U] = Tot++;
}

void AddQuery(int U, int V, int Index) {
	querys[QTot] = Query {V, QHead[U], Index};
	QHead[U] = QTot++;
	querys[QTot] = Query {U, QHead[V], Index};
	QHead[V] = QTot++;
}

void Tarjan(int Node) {
	Ancestor[Node] = Node;
	Vis[Node] = true;
	for (int i = Head[Node]; ~i; i = edges[i].Next) {
		if (Vis[edges[i].V]) {
			continue;
		}
		Tarjan(edges[i].V);
		Join(Node, edges[i].V);
		Ancestor[Find(Node)] = Node;
	}
	for (int i = QHead[Node]; ~i; i = querys[i].Next) {
		if (Vis[querys[i].Q]) {
			Answer[querys[i].Index] = Ancestor[Find(querys[i].Q)];
		}
	}
}

void Dfs(int Cur, int Parent) {
	Vis[Cur] = true;
	Sum[Cur] = Sum[Parent] + Cnt[Cur];
	for (int i = Head[Cur]; ~i; i = edges[i].Next) {
		if (Vis[edges[i].V]) {
			continue;
		}
		Dfs(edges[i].V, Cur);
	}
}

int main(int argc, char *argv[]) {
	Init();
	scanf("%d%d", &N, &P);
	for (int i = 1, U, V; i < N; ++i) {
		scanf("%d%d", &U, &V);
		Addedge(U, V);
	}
	for (int i = 1; i <= P; ++i) {
		scanf("%d%d", &inputs[i].U, &inputs[i].V);
		AddQuery(inputs[i].U, inputs[i].V, i);
	}
	Tarjan(1);
	for (int i = 1; i <= P; ++i) {
		Cnt[Answer[i]]++;
	}
	memset(Vis, false, sizeof(Vis));
	Dfs(1, 0);
	Ans = 0;
	for (int i = 1; i <= P; ++i) {
		Ans += Sum[inputs[i].U] + Sum[inputs[i].V] - 2 * Sum[Answer[i]];
	}
	for (int i = 1; i <= N; ++i) {
		Ans += 1LL * Cnt[i] * (Cnt[i] - 1) / 2;
	}
	printf("%lld\n", Ans);
    return 0;
}
全部评论

相关推荐

头像
03-18 09:09
Java
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务