To_Heart—题解——CF1016F

题目链接

Link.

题解

因为是 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+nowjdepj×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+nowjdepj×2 。发现我们只需要从后往前便利,再用个优先队列维护 n o w j − d e p j × 2 \mathrm{now_j} - \mathrm{dep_j} \times 2 nowjdepj×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

posted @ 2022-07-14 16:58  To_Heart  阅读(1)  评论(0编辑  收藏  举报  来源