[ AGC007 E ] Shik and Travel

题目

Atcoder

思路

007E01.png
007E02.png
007E03.png

代码

#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;
}
posted @ 2021-05-29 12:46  Protein_lzl  阅读(38)  评论(0编辑  收藏  举报