AT2172 [AGC007E] Shik and Travel

https://www.luogu.com.cn/problem/AT2172

挺妙的一道题

首先可以二分答案

考虑一个最暴力的可行性 d p dp dp
f [ u ] [ a ] [ b ] f[u][a][b] f[u][a][b]表示以 u u u为根的子树,第一个到的叶子结点的深度是 a a a,最后一个到的叶子结点深度是 b b b是否可行

考虑一个节点 x x x的左右儿子 u , v u,v u,v,把他们的dp值合并

f [ x ] [ a ] [ b ] ∣ = f [ u ] [ a ] [ i ] & f [ v ] [ j ] [ b ] & ( d i s ( i , j ) < = w ) f[x][a][b]|=f[u][a][i]\&f[v][j][b]\&(dis(i,j)<=w) f[x][a][b]=f[u][a][i]&f[v][j][b]&(dis(i,j)<=w)

再分析一个 f [ x ] [ a ] [ b ] f[x][a][b] f[x][a][b], 对于每个 a a a b b b一定越小越好只用保留一个就行了
对于 a a a是单调递增,那么 b b b一定是单调递减的

所以用 v e c t o r < p a i r < i n t , i n t > > vector<pair<int, int>> vector<pair<int,int>>保存下来这个 a , b a,b a,b
状态根据类似启发式合并可以证明状态数是 n l o n n nlonn nlonn

直接写即可加上排序可能3个log?
完全能过

code:

#include<bits/stdc++.h>
#define N 200050//😅😅😅😅😅😅
#define ll long long
#define pi pair<ll, ll>
#define mkp make_pair
#define fi first
#define se second
using namespace std;
int ch[N][2], val[N];
vector<pi> a[N];
void dfs(int u, ll X) {//😅😅😅😅
    vector<pi> ha, f;
    a[u].swap(ha);
    int ls = ch[u][0], rs = ch[u][1];
    if(!ls && !rs) {
        a[u].push_back(mkp(0, 0));
        return ;
    }
    dfs(ls, X), dfs(rs, X);

    ll t = X - val[ls] - val[rs];
    for(int i = 0, j = -1; i < a[ls].size(); i ++) {
        while(j + 1 < a[rs].size() && a[rs][j + 1].fi + a[ls][i].se <= t) j ++;
        if(j == -1 || j >= a[rs].size()) continue;
        f.push_back(mkp( a[ls][i].fi + val[ls], a[rs][j].se + val[rs]));
    }

    swap(ls, rs);
    for(int i = 0, j = -1; i < a[ls].size(); i ++) {
        while(j + 1 < a[rs].size() && a[rs][j + 1].fi + a[ls][i].se <= t) j ++;
        if(j == -1 || j >= a[rs].size()) continue;
        f.push_back(mkp( a[ls][i].fi + val[ls], a[rs][j].se + val[rs]));
    }

    sort(f.begin(), f.end());
    for(int i = 0; i < f.size(); i ++) {
        if(a[u].size() && a[u].back().se <= f[i].se) continue;
        a[u].push_back(f[i]);
    }
}
int check(ll X) {
    dfs(1, X);
    return a[1].size();
}
int n;
int main() {
    scanf("%d", &n);//😅😅😅😅
    ll l = -1, r = 0;
    for(int i = 2; i <= n; i ++) {
        int fa;
        scanf("%d%d", &fa, &val[i]);
        r += val[i];
        if(ch[fa][0]) ch[fa][1] = i;
        else ch[fa][0] = i;
    }

    while(l + 1 < r) {
        ll mid = (l + r) >> 1;
        if(check(mid)) r = mid;
        else l = mid;
    }
    printf("%lld", r);//😅😅😅
    return 0;
}
posted @ 2021-10-17 19:36  lahlah  阅读(26)  评论(0编辑  收藏  举报