2021ICPC上海 H、Life is a Game
阿新 • • 發佈:2022-05-11
賽場上沒想到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; }