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;
}