「解题报告」P9168 [省选联考 2023] 人员调度

发布时间 2023-04-10 11:47:12作者: APJifengc

很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!很套路的题啊!见过两遍的套路还是不会做啊!

第三遍见这个套路了!

问题中的“最大值”的限制很烦人,实际上由于我们本身要求的是最大化,那么我们可以将最大值的限制直接去掉,改为每个员工可以选择子树内的一个点,或者不选,求权值最大值。显然这个问题与原问题是等价的。

而这个问题就是一个最大权二分图匹配问题了。这是个很经典的问题,可以通过拟阵 / 模拟费用流证明,贪心地选较大的权值一定可以选出最优方案。那么我们只需要选出一个尽可能大的点集,满足所有点都能被匹配上,最大化权值和。

根据 Hall 定理,我们只需要满足第 \(k\) 个子树内的选择的点不超过 \(siz_k\) 个即可。(具体考虑左部点形成的集合对应着若干个子树,而对于右部的每种子树选择方案,左部点肯定要把所有包含在这个子树内的点全部选上,于是就是每个子树内的选取数量不超过 \(siz_k\) 个)

那么我们就有了一个贪心的做法:维护出每个点的子树内还能够选多少点,每次加入一个点,然后这个点到根的路径上均 \(-1\)。如果出现了 \(-1\),那么说明这些点的条件不能被满足,那么找出其中深度最大的点,再将这个点中的权值最小的点删去即可满足所有点的条件。

删除处理很显然,上线段树分治即可。最后复杂度 \(O(n \log^3 n)\),如果上全局平衡二叉树可以降到 \(O(n \log^2 n)\),不过应该没有必要。

代码不难写,然而我考场上还是不会做就是了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, k, m, t;
int x[MAXN], v[MAXN];
int beg[MAXN], ed[MAXN];
struct Graph {
    vector<int> e[MAXN];
    void add(int u, int v) {
        e[u].push_back(v), e[v].push_back(u);
    }
    int siz[MAXN], son[MAXN], dep[MAXN], fa[MAXN];
    void dfs1(int u, int pre) {
        fa[u] = pre, dep[u] = dep[pre] + 1, siz[u] = 1;
        for (int v : e[u]) if (v != pre) {
            dfs1(v, u);
            if (siz[v] > siz[son[u]]) son[u] = v;
            siz[u] += siz[v];
        }
    }
    int dfn[MAXN], idf[MAXN], top[MAXN], dcnt;
    int ed[MAXN];
    void dfs2(int u, int pre, int t) {
        top[u] = t, dfn[u] = ++dcnt, idf[dcnt] = u;
        if (son[u]) dfs2(son[u], u, t);
        for (int v : e[u]) if (v != pre && v != son[u]) {
            dfs2(v, u, v);
        }
        ed[u] = dcnt;
    }
} g;
struct SegmentTree1 { // siz_i
    struct Node {
        int tag;
        pair<int, int> mn;
    } t[MAXN << 2];
#define lc (i << 1)
#define rc (i << 1 | 1)
    void build(int i = 1, int l = 1, int r = n) {
        t[i].tag = 0;
        if (l == r) {
            t[i].mn = { g.siz[g.idf[l]], -l };
            return;
        }
        int mid = (l + r) >> 1;
        build(lc, l, mid), build(rc, mid + 1, r);
        t[i].mn = min(t[lc].mn, t[rc].mn);
    }
    void pushDown(int i) {
        if (t[i].tag) {
            t[lc].tag += t[i].tag;
            t[rc].tag += t[i].tag;
            t[lc].mn.first += t[i].tag;
            t[rc].mn.first += t[i].tag;
            t[i].tag = 0;
        }
    }
    void add(int a, int b, int v, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) {
            t[i].tag += v;
            t[i].mn.first += v;
            return;
        }
        int mid = (l + r) >> 1;
        pushDown(i);
        if (a <= mid) add(a, b, v, lc, l, mid);
        if (b > mid) add(a, b, v, rc, mid + 1, r);
        t[i].mn = min(t[lc].mn, t[rc].mn);
    }
    pair<int, int> query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) return t[i].mn;
        int mid = (l + r) >> 1;
        pushDown(i);
        if (b <= mid) return query(a, b, lc, l, mid);
        if (a > mid) return query(a, b, rc, mid + 1, r);
        return min(query(a, b, lc, l, mid), query(a, b, rc, mid + 1, r));
    }
    void chainAdd(int x, int v) {
        while (x) {
            add(g.dfn[g.top[x]], g.dfn[x], v);
            x = g.fa[g.top[x]];
        }
    }
    pair<int, int> chainQuery(int x) {
        pair<int, int> ret = { INT_MAX, 0 };
        while (x) {
            ret = min(ret, query(g.dfn[g.top[x]], g.dfn[x]));
            x = g.fa[g.top[x]];
        }
        return ret;
    }
} st1;
struct SegmentTree2 { // v_i
    pair<int, int> t[MAXN << 2];
    set<pair<int, int>> s[MAXN];
    void build(int i = 1, int l = 1, int r = n) {
        t[i] = { INT_MAX, l };
        if (l == r) {
            s[l].insert({ INT_MAX, 0 });
            return;
        }
        int mid = (l + r) >> 1;
        build(lc, l, mid), build(rc, mid + 1, r);
    }
    pair<int, int> query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a <= l && r <= b) return t[i];
        int mid = (l + r) >> 1;
        if (b <= mid) return query(a, b, lc, l, mid);
        if (a > mid) return query(a, b, rc, mid + 1, r);
        return min(query(a, b, lc, l, mid), query(a, b, rc, mid + 1, r));
    }
    void insert(int d, pair<int, int> v, int i = 1, int l = 1, int r = n) {
        if (l == r) {
            s[l].insert(v);
            t[i] = *s[l].begin();
            return;
        }
        int mid = (l + r) >> 1;
        if (d <= mid) insert(d, v, lc, l, mid);
        else insert(d, v, rc, mid + 1, r);
        t[i] = min(t[lc], t[rc]);
    }
    void erase(int d, pair<int, int> v, int i = 1, int l = 1, int r = n) {
        if (l == r) {
            s[l].erase(v);
            t[i] = *s[l].begin();
            return;
        }
        int mid = (l + r) >> 1;
        if (d <= mid) erase(d, v, lc, l, mid);
        else erase(d, v, rc, mid + 1, r);
        t[i] = min(t[lc], t[rc]);
    }
} st2;
long long ans;
struct SegmentTree3 {
    vector<int> t[MAXN << 2];
    void add(int a, int b, int v, int i = 1, int l = 0, int r = m) {
        if (a <= l && r <= b) {
            t[i].push_back(v);
            return;
        }
        int mid = (l + r) >> 1;
        if (a <= mid) add(a, b, v, lc, l, mid);
        if (b > mid) add(a, b, v, rc, mid + 1, r);
    }
    void solve(int i = 1, int l = 0, int r = m) {
        stack<pair<int, int>> st;
        for (int j : t[i]) {
            ans += v[j];
            st1.chainAdd(x[j], -1);
            st2.insert(g.dfn[x[j]], { v[j], j });
            st.push({j, 0});
            auto res = st1.chainQuery(x[j]);
            if (res.first == -1) {
                auto p = st2.query(-res.second, g.ed[g.idf[-res.second]]);
                ans -= p.first;
                int j = p.second;
                st1.chainAdd(x[j], 1);
                st2.erase(g.dfn[x[j]], { v[j], j });
                st.push({j, 1});
            }
        }
        if (l == r) {
            printf("%lld ", ans);
        } else {
            int mid = (l + r) >> 1;
            solve(lc, l, mid), solve(rc, mid + 1, r);
        }
        while (!st.empty()) {
            auto p = st.top(); st.pop();
            int j = p.first;
            if (p.second == 0) {
                ans -= v[j];
                st1.chainAdd(x[j], 1);
                st2.erase(g.dfn[x[j]], { v[j], j });
            } else {
                ans += v[j];
                st1.chainAdd(x[j], -1);
                st2.insert(g.dfn[x[j]], { v[j], j });
            }
        }
    }
} st3;
int main() {
    scanf("%*d%d%d%d", &n, &k, &m);
    for (int i = 2; i <= n; i++) {
        int p; scanf("%d", &p);
        g.add(p, i);
    }
    g.dfs1(1, 0), g.dfs2(1, 0, 1);
    st1.build(), st2.build();
    for (int i = 1; i <= k; i++) {
        scanf("%d%d", &x[i], &v[i]);
        beg[i] = 0, ed[i] = m;
    }
    for (int i = 1; i <= m; i++) {
        int op; scanf("%d", &op);
        if (op == 1) {
            ++k;
            scanf("%d%d", &x[k], &v[k]);
            beg[k] = i, ed[k] = m;
        } else {
            int x; scanf("%d", &x);
            ed[x] = i - 1;
        }
    }
    for (int i = 1; i <= k; i++) {
        st3.add(beg[i], ed[i], i);
    }
    st3.solve();
    return 0;
}