题意:给一张无向图,每个结点具有点权,每条边有边权,有一个声望值,到达新的结点可以让声望值加上该点的点权,想要通过一条边需要满足现有的声望值大于该边的边权。给出多组询问,每次询问告诉你起始位置和初始声望值,问最多能有多少声望值。
数据范围:点数1e5,询问数1e5
样例输入:
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
样例输出:16
36
分析:如果知道克鲁斯卡尔重构树的话,会发现题目中通过一条边的条件,需要现有的声望值大于边权,这个条件和克鲁斯卡尔重构树的性质,两个结点的LCA的权值为两点路径上最大边权,这个性质非常像,所以会考虑去建克鲁斯卡尔重构树,再接着考虑这棵树跟题意的关系。
↓样例对应的生成树长这样(做题时候的草稿,有点潦草,将就看看)
圆形的是实点,里面是序号,方形的是虚点,里面是路径上的最大权值。那么假如从8出发,能往上走两条边到达14,那么说明肯定也可以走过14左下的那些点10,5,6,所以能到达14,那此时的声望相当于14的子树中实点的权值和。那么我们就可以推导出能否通过一条边对初始权值的要求,mx[i]=i边父亲的虚点权值-i边连着的儿子的子树实点权值和。
又因为1e5次询问,肯定要考虑使用倍增来优化,就可以设置数组mx[i][j]表示从i结点出发往上走2^j条边,要求的最小初始声望值。而为了求出mx[i][0],我们需要维护每个结点的子树实点权值和,我用val[],表示,而重构树得到的限制边权虚点权值我用val2[]存储,就有了下面的代码 :
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn=2e5+10;
int val[maxn<<1],fa[maxn<<1],f[maxn][25],dep[maxn],val2[maxn];
int n,m,q,lg[maxn],mx[maxn][25];//mx表示从结点i往上走2^j条边需要的初始声望值
vector<int>G[maxn];//存重构树的图
//val是点,val2是最大的边权
struct edge{
int u,v,w;
}e[maxn<<1];
bool cmp(edge a,edge b){
return a.w<b.w;
}
int Find(int x){return x==fa[x]?fa[x]:fa[x]=Find(fa[x]);}
void dfs(int u,int fu,int deep){
dep[u]=deep;f[u][0]=fu;mx[u][0]=val2[fu]-val[u];
for(int i=1;i<=lg[dep[u]];i++){
f[u][i]=f[f[u][i-1]][i-1];
mx[u][i]=max(mx[u][i-1],mx[f[u][i-1]][i-1]);
}
for(auto v:G[u])
dfs(v,u,deep+1);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
for(int i=2;i<maxn;i++) lg[i]=lg[i>>1]+1;
cin>>n>>m>>q;
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<=2*n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
int u,v,w;cin>>u>>v>>w;
e[i]=edge{u,v,w};
}
int cnt=n;
sort(e+1,e+1+m,cmp);
for(int i=1;i<=m;i++){
int fu=Find(e[i].u),fv=Find(e[i].v),w=e[i].w;
if(fu==fv) continue;
cnt++;
fa[fu]=cnt;fa[fv]=cnt;
f[fu][0]=cnt;f[fv][0]=cnt;
val2[cnt]=w;//val2是虚点的限制最大边权
val[cnt]=val[fu]+val[fv];//val是子树的实点权值和
G[cnt].push_back(fu);
G[cnt].push_back(fv);
}
dfs(cnt,0,1);
while(q--){
int x,k;cin>>x>>k;
for(int i=lg[dep[x]];i>=0;i--){
if(k>=mx[x][i]&&f[x][i])
x=f[x][i];
}
cout<<k+val[x]<<endl;
}
//system("pause");
return 0;
}