[ AGC007 E ] Shik and Travel
题目
思路
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define int long long
#define a first
#define b second
using namespace std;
typedef pair<int, int> PII;
const int N = 200010;
int n, s[N][2], w[N][2];
vector<PII> f[N];
void DFS(int u, int mid) {
f[u].clear(); // 清空上次二分的数组
if (!s[u][0]) { f[u].push_back(make_pair(0, 0)); return; }
for (int i = 0; i < 2; i++) // 从下向上更新
if (s[u][i]) DFS(s[u][i], mid);
vector<PII> t; // 临时数组用来存储下面的答案
for (int rev = 0; rev < 2; rev++) { // 交换左右儿子做两次
int ls = s[u][0 ^ rev], rs = s[u][1 ^ rev];
int x = mid - w[u][0] - w[u][1];
for (int i = 0, j = -1; i < f[ls].size(); i++) {
// 双指针找应该的位置
while (j + 1 < f[rs].size() && f[rs][j + 1].a <= x - f[ls][i].b) j++;
if (j >= f[rs].size() || f[rs][j].a > x - f[ls][i].b) continue;
// 存到临时数组里, 别忘加上边权
t.push_back(make_pair(f[ls][i].a + w[u][0 ^ rev],
f[rs][j].b + w[u][1 ^ rev]));
}
}
// 保证 a 递增, b 递减的性质
sort(t.begin(), t.end());
for (int i = 0; i < t.size(); i++)
if (f[u].size() && f[u].back().b <= t[i].b) continue;
else f[u].push_back(t[i]);
}
bool check(int mid) { DFS(1, mid); return f[1].size() >= 1; }
signed main() {
cin >> n;
for (int i = 2, a, b; i <= n && cin >> a >> b; i++)
if (s[a][0]) s[a][1] = i, w[a][1] = b;
else s[a][0] = i, w[a][0] = b;
int l = 0, r = 1e9;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
return 0;
}