题目传送门
第一次过黑题,才打了48行
发
现
每
一
条
边
只
能
且
必
须
遍
历
两
遍
,
手
动
模
拟
或
直
接
证
明
可
以
知
道
一
个
结
论
发现每一条边只能且必须遍历两遍,手动模拟或直接证明可以知道一个结论
发现每一条边只能且必须遍历两遍,手动模拟或直接证明可以知道一个结论
如
果
遍
历
到
一
颗
子
树
,
那
么
就
一
定
要
把
这
棵
子
树
遍
历
完
才
能
遍
历
下
一
棵
如果遍历到一颗子树,那么就一定要把这棵子树遍历完才能遍历下一棵
如果遍历到一颗子树,那么就一定要把这棵子树遍历完才能遍历下一棵
这
样
就
很
容
易
想
到
利
用
树
形
D
P
的
方
法
,
同
时
又
具
有
单
调
性
,
可
以
用
二
分
,
记
二
分
的
数
值
为
v
a
l
u
e
这样就很容易想到利用树形DP的方法,同时又具有单调性,可以用二分,记二分的数值为value
这样就很容易想到利用树形DP的方法,同时又具有单调性,可以用二分,记二分的数值为value
发
现
这
个
题
目
的
树
是
一
个
二
叉
树
,
而
且
不
是
叶
子
就
一
定
有
2
个
儿
子
的
那
种
发现这个题目的树是一个二叉树,而且不是叶子就一定有2个儿子的那种
发现这个题目的树是一个二叉树,而且不是叶子就一定有2个儿子的那种
可
以
用
一
个
f
[
x
]
表
示
从
x
点
出
发
遍
历
到
a
叶
子
的
长
度
,
结
尾
再
遍
历
到
b
叶
子
的
长
度
的
所
有
满
足
≤
v
a
l
u
e
的
值
(
用
v
e
c
t
o
r
储
存
)
可以用一个f[x]表示从x点出发遍历到a叶子的长度,结尾再遍历到b叶子的长度的所有满足\leq value的值(用vector储存)
可以用一个f[x]表示从x点出发遍历到a叶子的长度,结尾再遍历到b叶子的长度的所有满足≤value的值(用vector储存)
只
需
要
用
儿
子
节
点
转
移
即
可
只需要用儿子节点转移即可
只需要用儿子节点转移即可
考
虑
如
何
转
移
考虑如何转移
考虑如何转移
记
一
颗
树
的
一
个
儿
子
为
l
s
,
另
外
一
个
为
r
s
,
两
个
点
的
边
分
别
为
l
v
,
r
v
.
很
显
然
,
f
[
x
]
的
(
a
,
b
)
一
个
在
l
s
子
树
内
,
一
个
在
r
s
子
树
内
记一颗树的一个儿子为ls,另外一个为rs,两个点的边分别为lv,rv.很显然,f[x]的(a,b)一个在ls子树内,一个在rs子树内
记一颗树的一个儿子为ls,另外一个为rs,两个点的边分别为lv,rv.很显然,f[x]的(a,b)一个在ls子树内,一个在rs子树内
f
[
x
]
=
{
(
f
[
x
.
l
s
]
.
l
f
t
+
l
v
,
f
[
x
.
r
s
]
.
l
f
t
)
(
f
[
x
.
l
s
]
.
l
f
t
+
l
v
,
f
[
x
.
r
s
]
.
r
g
h
)
(
f
[
x
.
l
s
]
.
r
g
h
+
l
v
,
f
[
x
.
r
s
]
.
l
f
t
)
(
f
[
x
.
l
s
]
.
r
g
h
+
l
v
,
f
[
x
.
r
s
]
.
r
g
h
)
这
里
的
f
是
个
结
构
体
,
代
表
着
所
有
的
点
对
,
l
f
t
表
示
点
对
的
左
值
,
r
g
h
表
示
点
对
的
右
值
f[x]=\begin{cases}(f[x.ls].lft+lv,f[x.rs].lft)\\(f[x.ls].lft+lv,f[x.rs].rgh)\\(f[x.ls].rgh+lv,f[x.rs].lft)\\(f[x.ls].rgh+lv,f[x.rs].rgh)\\这里的f是个结构体,代表着所有的点对,lft表示点对的左值,rgh表示点对的右值\end{cases}
f[x]=⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧(f[x.ls].lft+lv,f[x.rs].lft)(f[x.ls].lft+lv,f[x.rs].rgh)(f[x.ls].rgh+lv,f[x.rs].lft)(f[x.ls].rgh+lv,f[x.rs].rgh)这里的f是个结构体,代表着所有的点对,lft表示点对的左值,rgh表示点对的右值
画
个
图
就
能
明
白
:
画个图就能明白:
画个图就能明白:

但
是
很
容
易
就
想
到
这
样
很
显
然
是
不
可
能
全
部
转
移
,
因
为
方
案
数
实
在
太
多
了
,
但
是
一
定
会
有
很
多
的
冗
杂
的
信
息
考
虑
怎
么
优
化
,
可
以
发
现
对
于
两
个
决
策
(
v
1
a
,
v
1
b
)
)
,
(
v
2
a
,
v
2
b
)
,
如
果
v
1
a
<
v
2
a
,
v
1
b
<
v
2
b
,
那
么
后
面
的
那
个
决
策
一
定
是
没
有
必
要
的
但是很容易就想到这样很显然是不可能全部转移,因为方案数实在太多了,但是一定会有很多的冗杂的信息\\考虑怎么优化,可以发现对于两个决策(v1_a,v1_b)),(v2_a,v2_b),如果v1_a<v2_a,v1_b<v2_b,那么后面的那个决策一定是没有必要的
但是很容易就想到这样很显然是不可能全部转移,因为方案数实在太多了,但是一定会有很多的冗杂的信息考虑怎么优化,可以发现对于两个决策(v1a,v1b)),(v2a,v2b),如果v1a<v2a,v1b<v2b,那么后面的那个决策一定是没有必要的
那
么
这
样
子
的
优
化
后
,
排
序
一
定
是
具
有
单
调
性
的
,
假
设
按
第
一
关
键
字
排
序
,
就
是
第
一
个
数
持
续
递
增
,
第
二
个
数
持
续
递
减
,
那
么
就
可
以
发
现
对
于
两
个
儿
子
合
并
的
时
候
就
可
以
用
双
指
针
优
化
那么这样子的优化后,排序一定是具有单调性的,假设按第一关键字排序,就是第一个数持续递增,第二个数持续递减,那么就可以发现对于两个儿子合并的时候就可以用双指针优化
那么这样子的优化后,排序一定是具有单调性的,假设按第一关键字排序,就是第一个数持续递增,第二个数持续递减,那么就可以发现对于两个儿子合并的时候就可以用双指针优化
时
间
复
杂
度
O
(
n
l
o
g
2
n
)
时间复杂度O(nlog^2n)
时间复杂度O(nlog2n)不会证
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
#define ll long long
#define P pair<ll,ll>
#define ls lb[x].son
#define rs rb[x].son
#define lv lb[x].v
#define rv rb[x].v
#define fi first
#define se second
struct cow{int son,v;}lb[N],rb[N];
int n,a[N];vector<P>f[N];
void dfs(int x,ll v){
f[x].clear();
if(!ls){f[x].push_back((P){0,0});return ;}
vector<P>now;dfs(ls,v);dfs(rs,v);
for(int o=0;o<2;o++){
ll tmp=v-lv-rv;
for(int i=0,j=0;i<f[ls].size();i++){
while(j+1<f[rs].size()&&f[rs][j+1].fi+f[ls][i].se<=tmp)j++;
if(j>=f[rs].size()||f[rs][j].fi+f[ls][i].se>tmp)continue;
now.push_back((P){f[ls][i].fi+lv,f[rs][j].se+rv});
}swap(lv,rv);swap(ls,rs);
}sort(now.begin(),now.end());
for(int i=0;i<now.size();i++){
if(!f[x].empty()&&f[x].back().se<=now[i].se)
continue;f[x].push_back(now[i]);
}
}
bool check(ll mid){
dfs(1,mid);return f[1].size()>=1;
}
int main()
{
scanf("%d",&n);ll l=0,r=0;
for(int i=2;i<=n;i++){
int fa,v;scanf("%d%d",&fa,&v);
if(lb[fa].son)rb[fa]=(cow){i,v};
else lb[fa]=(cow){i,v};r+=v;
}
while(l<r){
ll mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%lld",l);return 0;
}