2021ICPC上海 H、Life is a Game

赛场上没想到kruskal重构树,只想到一个类似的并查集做法,调到结束也没调出来,不过还好铜尾(

时隔5个月突然有ec-final名额,然后又回来补题了

做法:

kruskal在用并查集合并树集合的时候,将新合并的集合作为一个新点,并连两条边到被合并的原集合,边权为合并的边权,集合大小直接相加。如下:

for (auto &now : e) {//枚举边
		int x = anc(now.x), y = anc(now.y), cost = now.cost;
		if (anc(x) == anc(y))continue;
		cnt++;//开一个新点
		fa[x] = cnt;//xy都指向cnt
		fa[y] = cnt;
		a[cnt] = a[x] + a[y];//集合大小直接相加
		G[cnt].push_back({ x,cost });//连两条边
		G[cnt].push_back({ y,cost });
	}

这样就得到了一个合并过程的有向树,每个点代表一个求最小生成树过程中的集合,根为当前的cnt(因为最后只剩一个集合了),而且每条边的边权与深度成反比,深度越大边权越小

因此可以预处理倍增和链区间最大值,去查询从x点出发最多能走到哪个集合

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;

const int maxn = 1e5 + 5;
const int inf = 1e9 + 7;
const ll lnf = 1e18;
const ll mod = 1e9 + 7;

int n, m, q;

struct edge {
	int x, y;
	int cost;
	bool operator <(const edge& e) {
		return cost < e.cost;
	}
};
ll a[maxn << 2];
int fa[maxn << 2];

int anc(int x) {
	return x == fa[x] ? x : fa[x] = anc(fa[x]);
}

vector<pair<int, int> >G[maxn << 2];//重构树,边权为跳跃限制,点权为最终答案

int deep[maxn << 2], f[21][maxn << 2];
ll mx[21][maxn << 2];

void dfs(int from, int fa) {//预处理倍增
	for (int i = 1; (1 << i) <= deep[from]; i++) {
		f[i][from] = f[i - 1][f[i - 1][from]];
		mx[i][from] = max(mx[i - 1][from], mx[i - 1][f[i - 1][from]]);
	}
	for (auto i : G[from]) {
		int to = i.first;
		if (to == fa)continue;
		deep[to] = deep[from] + 1;
		f[0][to] = from;
		mx[0][to] = i.second;
		dfs(to, from);
	}
}

ll query(int now, ll v) {
	while (mx[0][now] <= v + a[now]) {//和求LCA一样,但v + a[now]一直有变化
	//所以可能需要多跳几次
		for (int i = 20; i >= 0; i--)
			if (mx[i][now] <= v + a[now]) {
				now = f[i][now];
			}
	}
	return v + a[now];
}

int main()
{
	fastio;
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++)cin >> a[i];
	vector<edge>e;
	while (m--) {
		int x, y, z;
		cin >> x >> y >> z;
		e.push_back({ x,y,z });
	}
	sort(e.begin(), e.end());
	int cnt = n;
	for (int i = 1; i <= 4 * n; i++)fa[i] = i;
	for (auto &now : e) {
		int x = anc(now.x), y = anc(now.y), cost = now.cost;
		if (anc(x) == anc(y))continue;
		cnt++;
		fa[x] = cnt;
		fa[y] = cnt;
		a[cnt] = a[x] + a[y];
		G[cnt].push_back({ x,cost });
		G[cnt].push_back({ y,cost });
	}
	f[0][cnt] = cnt;
	for (int i = 1; i <= cnt; i++)
		for (int j = 0; j <= 20; j++)
			mx[j][i] = 1145141919810;
	dfs(cnt, 0);
	while (q--) {
		int x, k;
		cin >> x >> k;
		cout << query(x, k) << "\n";
	}

	return 0;

}
posted @ 2022-05-11 22:34  Lecoww  阅读(57)  评论(0编辑  收藏  举报