ZOJ Problem Set - 1141 Closest Common Ancestors(倍增法)

4 篇文章 0 订阅
#include <utility>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <bitset>
#include <map>
#include <iterator>
using namespace std;
#define clr(a,v) memset(a,v,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int INF = 0x7f7f7f7f;
const int maxn = 811;
const int POW = 10;
const double pi = acos(-1.0);
const double eps = 1e-8;
const int mod = 777777777;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<VVI> VVVI;
struct node {
	int v, next;
	node() {
	}
	node(int v, int next) :
			v(v), next(next) {
	}
} edge[maxn << 2];
int head[maxn], E;
void add_edge(int u, int v) {
	edge[E] = node(v, head[u]);
	head[u] = E++;
	edge[E] = node(u, head[v]);
	head[v] = E++;
}
struct LCA_Query {
	int p[POW][maxn], d[maxn];
	int v[maxn];
	bool vis[maxn];
	int cnt[maxn];
	void dfs(int u, int pre = 0) {
		p[0][u] = pre;
		int i, v;
		for (i = 1; i < POW; ++i) {
			p[i][u] = p[i - 1][p[i - 1][u]];
		}
		for (i = head[u]; ~i; i = edge[i].next) {
			v = edge[i].v;
			if (v == pre)
				continue;
			d[v] = d[u] + 1;
			dfs(v, u);
		}
	}
	void init(int rt) {
		clr(p, 0);
		clr(cnt, 0);
		d[rt] = 0;
		dfs(rt, rt);
	}
	int find(int u, int k) {
		for (int i = 0; i < POW; ++i) {
			if ((k >> i) & 1)
				u = p[i][u];
		}
		return u;
	}
	void query(int a, int b) {
		int i;
		if (d[a] > d[b])
			swap(a, b);
		if (d[b] > d[a])
			b = find(b, d[b] - d[a]);
		if (a != b) {
			for (i = POW - 1; i >= 0; --i) {
				if (p[i][a] != p[i][b]) {
					a = p[i][a];
					b = p[i][b];
				} 
			}
			a = p[0][a];
		}
		cnt[a]++;
	}
} LCA;
bool vis[maxn];
int main() {
	ios::sync_with_stdio(false);
	int n, u, v, k, i, j, m, a, b;
	while (~scanf("%d", &n)) {
		E = 0;
		clr(vis, false);
		clr(head, -1);
		for (i = 0; i < n; ++i) {
			scanf("%d:(%d)", &u, &k);
			for (j = 0; j < k; ++j) {
				scanf("%d", &v);
				vis[v] = true;
				add_edge(u, v);
			}
		}
		for (i = 1; i <= n; ++i) {
			if (!vis[i]) {
				LCA.init(i);
				break;
			}
		}
		scanf("%d", &m);
		for (i = 0; i < m; ++i) {
			scanf(" (%d,%d)", &a, &b);
			LCA.query(a, b);
		}
		for (i = 1; i <= n; ++i) {
			if (LCA.cnt[i]) {
				printf("%d:%d\n", i, LCA.cnt[i]);
			}
		}
	}
	return 0l;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值