题意:
给n个点,m条边的无向连通图。
每个点有权值,a1,a2,a3,...an,每条边有权值,w1,w2,w3,...wm。
经过每个点会获得ai的能量值,但仅可获得一次,经过第i条边时,能量值要大于等于wi,通过边时能量值不减少。
q次询问起始能量值为k,起始点为x,求可获得的最大能量值。
思路:
克鲁斯卡尔重构树
通过边能量值不减少,按照边权排序,建立重构树,将边权转化为点权,重构树的叶子节点为原树的节点,权值为0,其他节点为原先边权转化节点。
使用倍增来处理祖先,并且维护以某节点为根的子树的所有叶子节点的能量值和。
如果说能够向上跳,计算以当前拥有的能量值能够跳到的最远祖先节点,更新能量值,直到不能跳为止。
code:
const int N = 200005;//开二倍空间
int n,m,q;
int w[N],fa[N],a[N];//边权值,并查集father,子树能量值只和
int f[23][N];//重构树的倍增
int tot;//重构树的节点数
vector<int> kru[N];//重构树
struct edge
{
int x,y,z;
bool operator <(const edge& t)
{
return z < t.z;
}
}e[N];
void init()
{
for(int i=0;i<N;++i)fa[i]=i;
}
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
void solve()
{
cin >> n >> m >> q;
for(int i=1;i<=n;++i)cin >> a[i];
for(int i=1;i<=m;++i)cin >> e[i].x >> e[i].y >> e[i].z;
//-----------------重构树-----------------------------
sort(e+1,e+1+m);
init();
tot = n;
for(int i=1;i<=m;++i)
{
int fx=find(e[i].x),fy=find(e[i].y);
if(fx!=fy)
{
fa[fx]=++tot;
fa[fy]=tot;
w[tot]=e[i].z;//点权
a[tot]=a[fx]+a[fy];//子树威望和
f[0][fx]=tot,f[0][fy]=tot;
kru[tot].push_back(fx);
kru[tot].push_back(fy);
kru[fx].push_back(tot);
kru[fy].push_back(tot);
}
if(tot==2*n-1)break;
}
//--------------倍增-----------------------
f[0][tot]=tot;
for(int i=1;i<23;++i)
{
for(int j=1;j<=tot;++j)
{
f[i][j]=f[i-1][f[i-1][j]];
}
}
//----------------询问------------------------
while(q--)
{
int x,t;
cin >> x >> t;
int y = a[x] + t;
while(y>=w[f[0][x]] && x!=tot)//判断能否跳到父节点或已经跳到祖先节点
{
for(int i=22;i>=0;--i)
{
if(w[f[i][x]] <= y) x=f[i][x];
}
y=a[x]+t;//更新能量值
}
cout << y << endl;
}
}
int main()
{
ios :: sync_with_stdio(false);
solve();
return 0;
}