【AGC007E】Shik and Travel
【AGC007E】Shik and Travel
by AmanoKumiko
Description
给一颗\(N\)个点的树,每个点要么有两个儿子要么是叶子
任意选定一个叶子作为起点,然后要进行\(M-1\)天(\(M\)为叶子个数)的旅行
每次去到一个没经过的叶子,同时在整个过程中每条边的经过次数不超过\(2\)
求最小的最大的每天的旅行的距离
Input
第一行一个数\(N\)
然后\(N-1\)行,第\(i\)行读入\(fa_{i+1},a_{i+1}\)
Output
一行一个整数表示答案
Sample Input
9
1 26166
1 278
2 23731
2 17834
5 4105
5 19470
6 28803
6 3445
Sample Output
52378
Data Constraint
\(2\le N\le 10^5\),\(0\le a_i\le 10^5\)
Solution
先二分答案
容易发现一定会遍历完某一个子树再遍历它的另一个子树
那我们设一个二元组\((u,v)\)表示在某个子树中,存在起点为\(u\),最后到达\(v\),
且中间的所有距离不大于\(mid\)的路径
那么直接做就能得到\(O(n^2)\)的好成绩
我们考虑优化
令合并的起点为\((u1,v1)\),终点为\((u2,v2)\)
把柿子写出来\(dis_{v1}+dis_{u2}-2dis_{root}\le mid\)
那么作为起点时,钦定\(u1\)则需要最小化\(dis_{v1}\)
作为终点同理
这样删除掉不优的状态后就能得到\(O(nlog^2n)\)的复杂度
合并可以用双指针(但我懒所以不想写
Code
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define inf 10000000000000
#define LL long long
#define N 100010
LL le,ri,dis[N],mid;
int n,a[N],ls[N],rs[N];
struct point{int u,v;};
bool cmp1(const point&le,const point&ri){
return dis[le.u]<dis[ri.u];
}
bool cmp2(const point&le,const point&ri){
return dis[le.v]<dis[ri.v];
}
vector<point>s1[N],s2[N];
void dfs(int x){
if(ls[x]){
dis[ls[x]]=dis[x]+a[ls[x]];dfs(ls[x]);
dis[rs[x]]=dis[x]+a[rs[x]];dfs(rs[x]);
}
}
int tp;
void calc(int x){
tp++;
if(ls[x]){
calc(ls[x]);calc(rs[x]);
if(s2[ls[x]].size()&&s1[rs[x]].size()){
int lstpos=0;
F(i,0,s2[ls[x]].size()-1){
LL Min=inf;int pos=0;
F(j,0,s1[rs[x]].size()-1){
if(dis[s1[rs[x]][j].u]<=dis[x]*2+mid-dis[s2[ls[x]][i].v]){
if(dis[s1[rs[x]][j].v]<Min)Min=dis[s1[rs[x]][j].v],pos=s1[rs[x]][j].v;
}else break;
}
if(pos&&pos!=lstpos)s2[x].push_back((point){s2[ls[x]][i].u,pos}),lstpos=pos;
}
lstpos=0;
F(j,0,s1[rs[x]].size()-1){
LL Min=inf;int pos=0;
F(i,0,s2[ls[x]].size()-1){
if(dis[s2[ls[x]][i].v]<=dis[x]*2+mid-dis[s1[rs[x]][j].u]){
if(dis[s2[ls[x]][i].u]<Min)Min=dis[s2[ls[x]][i].u],pos=s2[ls[x]][i].u;
}else break;
}
if(pos&&pos!=lstpos)s1[x].push_back((point){pos,s1[rs[x]][j].v}),lstpos=pos;
}
}
if(s2[rs[x]].size()&&s1[ls[x]].size()){
int lstpos=0;
F(i,0,s2[rs[x]].size()-1){
LL Min=inf;int pos=0;
F(j,0,s1[ls[x]].size()-1){
if(dis[s1[ls[x]][j].u]<=dis[x]*2+mid-dis[s2[rs[x]][i].v]){
if(dis[s1[ls[x]][j].v]<Min)Min=dis[s1[ls[x]][j].v],pos=s1[ls[x]][j].v;
}else break;
}
if(pos&&pos!=lstpos)s2[x].push_back((point){s2[rs[x]][i].u,pos}),lstpos=pos;
}
lstpos=0;
F(j,0,s1[ls[x]].size()-1){
LL Min=inf;int pos=0;
F(i,0,s2[rs[x]].size()-1){
if(dis[s2[rs[x]][i].v]<=dis[x]*2+mid-dis[s1[ls[x]][j].u]){
if(dis[s2[rs[x]][i].u]<Min)Min=dis[s2[rs[x]][i].u],pos=s2[rs[x]][i].u;
}else break;
}
if(pos&&pos!=lstpos)s1[x].push_back((point){pos,s1[ls[x]][j].v}),lstpos=pos;
}
}
sort(s1[x].begin(),s1[x].end(),cmp1);
sort(s2[x].begin(),s2[x].end(),cmp2);
}else{
s1[x].push_back((point){x,x});
s2[x].push_back((point){x,x});
}
}
bool check(){
tp=0;
F(i,1,n)s1[i].clear(),s2[i].clear();
calc(1);
return s1[1].size();
}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d",&n);
F(i,2,n){
int fat;
scanf("%d%d",&fat,&a[i]);
ls[fat]?rs[fat]=i:ls[fat]=i;
}
dfs(1);
le=-1;ri=10000000001;
while(le<ri-1)mid=le+ri>>1,check()?ri=mid:le=mid;
printf("%d",ri);
return 0;
}