题目链接
题解
因为是 1 1 1 到 n n n 在树上的最短路,所以一定是 1 1 1 到 n n n 这条链,我们先把链拉出来,那么链上的每一个节点可以看做挂上了剩余的不在链上的节点。
如果链上的一个节点挂着的节点超过或等于两个,那么我们可以选择在这两个之间连边,所以无论询问所给的边是什么都不会对答案产生影响。这种情况特判即可。
那么现在我们的节点就最多只会挂一个不在链上的节点了。
因为只有能连边才可能会对答案产生影响,所以考虑哪些点之间可以连边。容易发现,只有当链上的两个点相邻且他们都没有挂其他点的时候,他们不可以连边。
为了表现连边的贡献,定义 d e p \mathrm {dep} dep 数组, d e p i \mathrm{dep_i} depi表示从 1 1 1 开始到 i i i 的距离;定义 n o w \mathrm {now} now 数组, n o w i \mathrm{now_i} nowi表示从 1 1 1 开始到 i i i 的距离,如果 i i i 挂了点,那么 n o w i \mathrm{now_i} nowi 再加上 i i i 到它挂的点的距离。
设在 i , j i,j i,j 间连了一条长度为 s u m \mathrm {sum} sum 的边 ( i < j ) (i<j) (i<j)。
因为在 i , j i,j i,j 连了一条边,所以构成了一个环,那么从不在链上的边走到 n n n 的代价就为 d e p n + n o w i + n o w j − d e p j × 2 + s u m \mathrm {dep_n} + \mathrm {now_i} + \mathrm{now_j} - \mathrm{dep_j} \times 2 + \mathrm{sum} depn+nowi+nowj−depj×2+sum
发现除了 s u m \mathrm {sum} sum 以外其他东西都是固定的所以我们可以先求出来,然后再加上读入的 s u m \mathrm {sum} sum,最后和 d e p n \mathrm {dep_n} depn 比较一个最小值输出即可。
所以我们现在只需要考虑怎么求出
n
o
w
i
+
n
o
w
j
−
d
e
p
j
×
2
\mathrm {now_i} + \mathrm{now_j} - \mathrm{dep_j} \times 2
nowi+nowj−depj×2 。发现我们只需要从后往前便利,再用个优先队列维护
n
o
w
j
−
d
e
p
j
×
2
\mathrm{now_j} - \mathrm{dep_j} \times 2
nowj−depj×2 的最大值即可其实可以单调队列但我懒
code.
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int INF=1e15;
int n,m;
struct zz{
int u,w;
};
bool operator <(zz x,zz y){
if(x.u==y.u) return x.w>y.w;
return x.u<y.u;
}
vector<zz> v[300005];
bool f[300005];
int dep[300005],Max[300005],siz[300005];
void DFS_1(int x,int fa){
if(x==n) f[x]=1;
Max[x]=dep[x];siz[x]=1;
for(const auto &qwq:v[x]) if(qwq.u^fa){
int y=qwq.u,w=qwq.w;
dep[y]=dep[x]+w;
Max[x]=max(Max[x],Max[y]);
DFS_1(y,x);
if(f[y]) f[x]=1;
else siz[x]+=siz[y];
}
}
vector<int> q;
int now[300005];
void DFS_2(int x,int fa){
q.push_back(x);now[x]=dep[x];
for(auto qwq:v[x]) if(!f[qwq.u]) now[x]+=(Max[qwq.u]-dep[x]);
for(auto qwq:v[x]) if(f[qwq.u]&&(qwq.u^fa)) DFS_2(qwq.u,x);
}
priority_queue<zz> qq;
signed main(){
cin>>n>>m;
for(int i=1,x,y,z;i<n;i++)
scanf("%lld%lld%lld",&x,&y,&z),v[x].push_back((zz){y,z}),v[y].push_back((zz){x,z});
dep[1]=0;DFS_1(1,0);
for(int i=1;i<=n;i++){
if(f[i]) if(siz[i]>=3){
for(int k=1;k<=m;k++) printf("%lld\n",dep[n]); return 0;
}
}
DFS_2(1,0);
int ans=now[1]+now[n]-dep[n]*2;
for(int i=q.size()-1;i>=0;i--){
zz qwq;
if(!qq.size()) goto Fuck;
qwq=qq.top();
if(qwq.w==q[i+1]&&siz[qwq.w]<2&&siz[q[i]]<2){
qq.pop();
if(!qq.size()){ qq.push(qwq);goto Fuck; }
else{ zz qaq=qq.top();qq.push(qwq);qwq=qaq; }
}
ans=max(ans,qwq.u+now[q[i]]);
Fuck:;qq.push((zz){now[q[i]]-dep[q[i]]*2,q[i]});
}
for(int i=0;i<q.size();i++){
for(int j=0;j<i;j++) if((j==i-1&&siz[i]==1&&siz[j]==1)) ans=max(now[q[j]]+now[q[i]]-dep[q[i]]-dep[q[i]],ans);
}
for(int i=1,x;i<=m;i++){
scanf("%lld",&x),printf("%lld\n",min(dep[n],dep[n]+ans+x));
}
return 0;
}
哦对了,这份代码只能在私下用,在西西弗的比赛中用会被封号的/jk