kruskal重构树
具体应用
H - Life is a Game
题意:给定n条边和权值以及点的权值,以及q个询问,每次询问提供点编号x以及初始值,每次经过点可以获得其权值,问从x出发最多可以获得多少值(值大于等于边的权值才能经过这条边)。
思路 :这题本来想着是用堆优化的类kruskal做的,每次将新加的边加入集合,然后往外拓展找新边,但是T了,后来训练赛结束后发现kruskal重构树加离线可以解,kruskal重构树可以参考严格鸽的文章
https://zhuanlan.zhihu.com/p/567569277
大概意思就是先跑一遍kruskal,在跑的过程中,边建一个节点连接两个点,然后每次新加边都增加节点,重构树的叶子节点就是所有的点,然后网上都是边节点,且边权值自下而上逐级递增,那么就可以方便找到第一个不满足当前权值加初始权值大于等于边权值的边了,然后利用离线优化,从初始权值小的开始找,这样就能保证每次找到的级数高于前面的,方便记忆化搜索。
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define endl "\n"
#define debug freopen("C:/Users/HBH/Desktop/1.txt","w",stdout);
struct edge
{
ll id;
ll u;
ll v;
ll w;
};
struct que
{
ll u;
ll k;
ll ans;
};
que ques[200001];
ll n, m, q;
ll idx;
ll val[200001];
vector<ll>tri[200001];
ll F[200001];
ll V[200001];
ll sum[200001];
ll up[200001];
ll tag[200001];
vector<edge>e;
bool cmp(edge a, edge b)
{
return a.w < b.w;
}
bool cmpque(que a, que b)
{
return a.k < b.k;
}
bool cmpid(que a, que b)
{
return a.id < b.id;
}
ll find(ll x)//并查集辅助kruskal
{
if (x == F[x])return x;
else return F[x]=find(F[x]);
}
void init(ll u, ll v, ll w)
{
ll a = find(u);
ll b = find(v);//找到当前节点的最上级父亲
if (a == b)return ;
F[a] = idx;
F[b] = idx;//连接两个集合到一个新的边点
F[idx] = idx;
V[idx] = w;//记录边权值
tri[idx].push_back(a);//记录边的孩子节点,待会需要建立重构树
tri[idx].push_back(b);
idx++;
}
ll dfs(ll pos)
{
if (pos <= n)
{
sum[pos] = val[pos]; return sum[pos];//记录每个点的点权值和
}
ll s = 0;
for (int i = 0; i < tri[pos].size(); i++)
{
F[tri[pos][i]] = pos;
s += dfs(tri[pos][i]);
}
sum[pos] = s;
return s;
}
ll check(ll u, ll k)
{
//cout << k << " " << u << endl;
if (up[u])u = up[u];//快速跳到当前节点能跳到的最高节点
if(u==idx-1){up[u]=u;return u;}//到达根节点了,不能再往上跳了
if (k + sum[u] >= V[F[u]])//满足条件
{
up[u] = check(F[u], k);
}
else
{up[u] = u;}//不满足条件,直接截止
return up[u];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
{
cin >> val[i];
}
idx = n + 1;
for (int i = 1; i <= n; i++)
{
F[i] = i;
}
for (int i = 0; i < m; i++)
{
ll u, v, w;
cin >> u >> v >> w;
e.push_back({u, v, w});
}
sort(e.begin(), e.end(), cmp);
for (int i = 0; i < m; i++)
{
init(e[i].u, e[i].v, e[i].w);
}
ll root = idx - 1;
dfs(root);
for (int i = 0; i < q; i++)
{
ll u, v;
cin >> u >> v;
ques[i] = {i u, v};
}
sort(ques, ques + q, cmpque);//按询问的初始值从小到大排
for (ll i = 0; i < q; i++)
{
ques[i].ans=sum[check(ques[i].u, ques[i].k)]+ques[i].k;//直接记录答案,防止同一个点查询两次干扰答案
}
sort(ques, ques + q, cmpid);//按原序排回
for (int i = 0; i < q; i++)
{
cout << ques[i].ans << endl;
}
}