[AGC007E] Shik and Travel 题解
考虑二分答案,问题转化为判定答案是否
设
有转移方程
直接做这个东西显然是会爆炸的,考虑优化。
注意到如果
设点
关于
\sum F=\mathcal{O}(n\log n) 的一个证明:由
F_x\le 2\min(F_{l_x},F_{r_x})\le F_{l_x}+F_{r_x} 可以得到F_x\le\mathrm{size}_x ,因此有\sum F\le 2\sum\min(F_{l_x},F_{r_x})\le 2\sum\min(\mathrm{size}_{l_x},\mathrm{size}_{r_x}) ,而最后这个式子就是经典的启发式合并,因此\sum F=\mathcal{O}(n\log n) 。
跑两遍双指针可以得到所有合法状态,合并后去除没用的状态即可。时间复杂度
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
using LL = long long;
using Pll = pair<LL, LL>;
const int kN = (1 << 17) + 1;
struct E {
int y, w;
};
int n;
vector<E> e[kN];
vector<Pll> f[kN];
void D(int x, LL v) {
if (e[x].empty()) {
f[x].emplace_back(0, 0);
return;
}
D(e[x][0].y, v), D(e[x][1].y, v);
vector<Pll> _f[2];
for (int o = 0; o < 2; ++o) {
int l = e[x][o].y, r = e[x][!o].y;
int lw = e[x][o].w, rw = e[x][!o].w;
for (int i = 0, j = 0; i < f[l].size(); ++i) {
for (; j + 1 < f[r].size() && f[l][i].second + f[r][j + 1].first + lw + rw <= v; ++j) {
}
if (j < f[r].size() && f[l][i].second + f[r][j].first + lw + rw <= v) {
_f[o].emplace_back(f[l][i].first + lw, f[r][j].second + rw);
}
}
}
vector<Pll> rf(_f[0].size() + _f[1].size());
merge(_f[0].begin(), _f[0].end(), _f[1].begin(), _f[1].end(), rf.begin());
for (Pll i : rf) {
if (f[x].empty() || f[x].back().first < i.first && f[x].back().second > i.second) {
f[x].push_back(i);
}
}
}
int main() {
ios_base::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 2, y, w; i <= n; ++i) {
cin >> y >> w;
e[y].push_back({i, w});
}
LL l = 0, r = 1e12;
while (l < r) {
LL m = l + r >> 1;
for (int i = 1; i <= n; ++i) {
f[i].clear();
}
D(1, m);
if (f[1].empty()) {
l = m + 1;
} else {
r = m;
}
}
cout << l;
return 0;
}