1. 程式人生 > 其它 >2021ICPC上海 H、Life is a Game

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) {
	//cout << from << " " << deep[from] << endl;
	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]) {
		//cout<<now<<" " << mx[0][now] << " " << v + a[now] << endl;
		for (int i = 20; i >= 0; i--)
			if (mx[i][now] <= v + a[now]) {
				//cout<<i<<" " << now << " "<<f[i][now]<<" " << mx[i][now] << " " << v + a[now] << endl;
				now = f[i][now];
			}
	}
	return v + a[now];
}

int main()
{
	/*
	8 10 2
3 1 4 1 5 9 2 6
1 2 7
1 3 11
2 3 13
3 4 1
3 6 31415926
4 5 27182818
5 6 1
5 7 23333
5 8 55555
7 8 37
1 7
8 30
	*/
	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);
	/*for (int j = 0; j <= 20; j++)
		cout << f[j][1] << " " << mx[j][1] << endl;;
	cout << endl;*/
	//cout << 1 << endl;
	while (q--) {
		int x, k;
		cin >> x >> k;
		cout << query(x, k) << "\n";
	}

	return 0;

}