[AGC007E] Shik and Travel 题解

这道题目有一个很经典的性质,每一条边经过两边意味着你每次进入一个子树出来的时候子树内的所有边都需要经过两边,那么一个节点向上有两种长度,一个点有两个儿子总共是四种长度,要合并其中的两种长度(可能产生贡献),保留另外两种长度。

我们考虑二分答案,那么是否可以合并两种长度我们已经知道了,需要关注的就是保留的两种长度。有一个显然的结论,保留的时候一定是左边保留一条边,右边保留一条边,假定我们已经保留了右边的一条边,那么左侧和右边另外一条边合并的只要可以合并就行了,另外一条边是越小越好(应为只要可以合并,我们保留的并不是最小的,记右边保留的是\(a\),左边保留的最小的为\(b\),你现在考虑的是\(c\),则\((a,c) > (a,b)\),在\((a,c)\)可行的时候\((a,b)\)一定可行,不需要考虑了。)。

我们来分析一下复杂度,假设两个儿子所有的种类(也就是形如\((a,b)\)这样的二元组的个数)分别为\(L\)\(R\)。考虑让个数少的去匹配个数多的,则本节点的种类数不会超过\(2*min(L,R)\),复杂度同\(dsu on tree\)的证明可以知道不会超过\(O(n \log_2 n )\)

代码:

#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int maxn=140000;
int n,x,y,l,r,ans,jie,ls,rs,L,R,val,flag,zg;
vector<int>tu[maxn],tu1[maxn];
vector<pii>shu[maxn];
void dfs(int q){
	if(!tu[q].size()){
		shu[q].push_back({0,0});
		return;
	}
	dfs(tu[q][0]);
	dfs(tu[q][1]);
	ls=tu[q][0],rs=tu[q][1];
	R=-1;
	flag=0;
	val=1e9;
	zg=tu1[q][0]+tu1[q][1];
	for(int i=shu[ls].size()-1;i>=0;i--){
		while(R+1<shu[rs].size()&&shu[rs][R+1].first+shu[ls][i].first+zg<=jie){
			R++;
			flag=1;
			val=min(val,shu[rs][R].second);
		}
		if(flag){
			shu[q].push_back({val+tu1[q][1],shu[ls][i].second+tu1[q][0]});
			shu[q].push_back({shu[ls][i].second+tu1[q][0],val+tu1[q][1]});
		}
	}
	sort(shu[q].begin(),shu[q].end());
	return;
}
int check(int q){
	jie=q;
	for(int i=1;i<=n;i++){
		vector<pii>().swap(shu[i]);
	}
	dfs(1);
	if(shu[1].size()){
		return 1;
	}
	return 0;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=2;i<=n;i++){
		cin>>x>>y;
		tu[x].push_back(i);
		tu1[x].push_back(y);
	}
	l=0;
	r=1e9;
	while(l<=r){
		int mid=(l+r)/2;
		if(check(mid)){
			ans=mid;
			r=mid-1;
		}
		else{
			l=mid+1;
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2025-04-30 10:52  特别之处  阅读(15)  评论(0)    收藏  举报