【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
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]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]>1,check()?ri=mid:le=mid;
printf("%d",ri);
return 0;
}