[CSP-S 2023] 種樹

pipipipipi43發表於2024-06-27

25分版本 覆蓋性質 A

點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll, ll> pir;
const int N = 2e5 + 5;
const ll INF = 1e18;
int n, m, r;
std::vector<int> g[N];

int fa[N], sz[N], wc[N], dep[N], vistime;
void dfs1(int u, int f)// 獲得 子樹大小,深度,父節點,重兒子等資訊
{
    fa[u] = f;
    sz[u] = 1;
    dep[u] = dep[f] + 1;
    for(auto v : g[u])
    {
        if(v == f)continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if(sz[v] > sz[wc[u]])wc[u] = v;
    }
}
int dfn[N], rdfn[N], top[N];
void dfs2(int u, int Top)// 獲取 dfs 序當前重鏈的鏈頭
{
    dfn[u] = ++vistime;
    rdfn[vistime] = u;
    top[u] = Top;
    if(wc[u] != 0)
    {
        dfs2(wc[u], Top);
        for(auto v : g[u])
        {
            if(v == fa[u] || v == wc[u])continue;
            dfs2(v, v);
        }
    }
}
ll a[N], b[N], c[N], val[N];
struct Info
{
    ll sum;
};
Info operator + (Info a, Info b)
{
    Info rt;
    rt.sum = max(a.sum, b.sum);
    return rt;
}
struct Seg_Tree
{
    Info tree[N << 2];
    ll tag[N << 2];
    void build(int l, int r, int rt)
    {
        if(l == r)
        {
            int id = rdfn[l];
            tree[rt].sum = a[id] / b[id] + dep[id];
            return;
        }
        int mid = l + r >> 1;
        build(l, mid, rt << 1);
        build(mid + 1, r, rt << 1 | 1);
        tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
    }
    void pushdown(int l, int r, int rt)
    {
        if(tag[rt])
        {
            tag[rt << 1] += tag[rt];
            tree[rt << 1].sum += tag[rt];

            tag[rt << 1 | 1] += tag[rt];
            tree[rt << 1 | 1].sum += tag[rt];
            tag[rt] = 0;
        }
    }

    void update(int l, int r, int rt, int L, int R, ll val)
    {
        if(L <= l && r <= R)
        {
            tree[rt].sum += val;
            tag[rt] += val;
            return;
        }
        pushdown(l, r, rt);
        int mid = l + r >> 1;
        if(L <= mid)update(l, mid, rt << 1, L, R, val);
        if(R > mid) update(mid + 1, r, rt << 1 | 1, L, R, val);
        tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
    }
    Info query(int l, int r, int rt, int L, int R)
    {
        if(L <= l && r <= R)
        {
            return tree[rt];
        }
        pushdown(l, r, rt);
        int mid = l + r >> 1;
        if(R <= mid)return query(l, mid, rt << 1, L, R);
        else if(L > mid)return query(mid + 1, r, rt << 1 | 1, L, R);
        return query(l, mid, rt << 1, L, R) + query(mid + 1, r, rt << 1 | 1, L, R);
    }
} T;
// 理論上每一個子樹只會查詢一次
// set<pir>sa;
// 可是又不是不變的
// 一共走了 x 步,我這條路走了 y 部
// 相當於

void solve()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)cin >> a[i] >> b[i] >> c[i];
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    if(count(c + 1, c + n + 1, 0) != n)assert(1);
    dfs1(1, 0);
    dfs2(1, 0);
    T.build(1, n, 1);
    std::vector<ll> ans(n + 1);
    set<pir>sa;
    T.update(1, n, 1, dfn[1], dfn[1], -INF);
    for(int x : g[1])
    {
        ll val = T.query(1, n, 1, dfn[x], dfn[x] + sz[x] - 1).sum;
        sa.insert({val, x});
    }
    int now = 0;
    while(!sa.empty())
    {
        auto it = sa.end();
        --it;
        auto [val, u] = *it;
        ans[u] = ++now;
        sa.erase(it);
        T.update(1, n, 1, dfn[u], dfn[u] + sz[u] - 1, -1);
        for(auto x : g[u])
        {
            if(x == fa[u])continue;
            ll val = T.query(1, n, 1, dfn[x], dfn[x] + sz[x] - 1).sum;
            sa.insert({val, x});
        }
        T.update(1, n, 1, dfn[u], dfn[u], -INF);
    }
    ll maxx = 0;
    for(int i = 1; i <= n; i++)
    {
        ll tot = ans[i] + a[i] / b[i];
        if(a[i] % b[i])tot++;
        maxx = max(maxx, tot);
    }
    cout << maxx << '\n';
}

int main()
{
    // freopen("P4779_1.in", "r", stdin);
    //freopen("pi.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    solve();
}
/*
找一個dfs序使得 dfn[i] +a[i] 的最大值最小
可以發現  dfs 序最後結束的位置一定是葉子節點
這玩意一定是個貪心 + 資料結構之類的
考慮向哪條路走會更優?
肯定考慮向當前最大的地方走啊,然後剩下的所有沒到的點 time+1
*/