[APIO2021] 封闭道路 题解

思路

一道很好口糊,但是实现细节颇多的题(时间复杂度容易假掉)。

我们先考虑固定 $k$ 的情况,用 $dp_{u,0}$ 表示删除点 $u$ 到其父亲的连边的最小删边代价,用 $dp_{u,1}$ 表示不删除点 $u$ 到其父亲的连边的最小删边代价。对于儿子 $v$ 有两种转移方式,第一种 $dp_{v,0}+cost(u,v)$,第二种 $dp_{v,1}$。发现直接转移要多加一维,和树上背包类似。但是复杂度会爆炸。故而我们选择一种带反悔的方法,一开始所有都选 $dp_{v,1}$,再把一些 $1$ 的换成 $0$ 的。那么只要用堆 $dp_{v,0}+cost(u,v)-dp_{v,1}$ 即可。

之后我们考虑从小到大枚举 $k$,对于所有度数大于 $k$ 的点的联通块按上述简易版方法跑一遍树 DP。那些不用跑的点我们在枚举到的度数等于它时就已经把其删除,对所有它连出去的点的大根堆放入了它的权值了(注意当前进行树 DP 时已经删除的点不用操作就满足,故而不会产生额外贡献)。

时间复杂度 $O(\text{度数})$,若要更具体的可以就代码分析。

代码

切记不要用 C++14 提交,不然会满屏 CE(GCC 9 编译版本过低)。

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define L(i, a, b) for(int i = a; i <= b; i++)
#define R(i, a, b) for(int i = a; i >= b; i--)
using namespace std;
vector<ll> minimum_closure_costs(int N, vector<int> U, vector<int> V, vector<int> W);
const int N = 1e5 + 10; 
int n, dg, de[N], id[N], vis[N]; ll sm, dp[N][2];
vector<pair<int, int> > e[N]; vector<ll> a, d, ans;
struct Large_Root_Heap{
    priority_queue<ll> q, d; ll sz, s;
    void upd(){while(!d.empty() && q.top() == d.top()) d.pop(), q.pop();}
    ll top(){upd(); return q.top();}
    void ins(ll x){q.push(x), s += x, sz++;}
    void del(ll x){d.push(x), s -= x, sz--;}
} h[N];
bool cmp(int i, int j){return de[i] < de[j];}
bool cmpe(pair<int, int> &x, pair<int, int> &y){return de[x.first] > de[y.first];}
void dfs(int u, int pre){
    int cnt = de[u] - dg; ll ss = 0;
    while(h[u].sz && h[u].sz > cnt)
        a.pb(h[u].top()), h[u].del(h[u].top());
    for(auto &ne: e[u]){
        int v = ne.first; if(de[v] <= dg) break;
        if(v == pre) continue; dfs(v, u);
    }
    a.clear(), d.clear(), vis[u] = dg;
    for(auto &ne: e[u]){
        int v = ne.first, w = ne.second;
        if(v == pre) continue; if(de[v] <= dg) break;
        ll vl = dp[v][0] + w - dp[v][1]; ss += dp[v][1];
        if(vl <= 0) cnt--, ss += vl;
        else h[u].ins(vl), d.pb(vl);
    }
    while(h[u].sz && h[u].sz > cnt)
        a.pb(h[u].top()), h[u].del(h[u].top());
    ss += h[u].s, dp[u][1] = ss;
    dp[u][0] = ss - ((cnt > 0 && h[u].sz)? h[u].top() : 0);
    for(auto &vl: a) h[u].ins(vl);
    for(auto &vl: d) h[u].del(vl);
}
vector<ll> minimum_closure_costs(int N, vector<int> U, vector<int> V, vector<int> W){
    n = N, ans.resize(n);
    L(i, 1, n - 1){
        int u = U[i - 1] + 1, v = V[i - 1] + 1, w = W[i - 1];
        e[u].pb(make_pair(v, w)), de[u]++;
        e[v].pb(make_pair(u, w)), de[v]++, ans[0] += w;
    }
    L(i, 1, n) id[i] = i, sort(e[i].begin(), e[i].end(), cmpe);
    sort(id + 1, id + n + 1, cmp);
    for(int i = dg = 1; dg < n; dg++){
        while(i <= n && de[id[i]] <= dg){
            for(auto ne: e[id[i]]) h[ne.first].ins(ne.second);
            i++;
        }
        L(j, i, n) if(vis[id[j]] != dg)
            dfs(id[j], 0), ans[dg] += dp[id[j]][1];
    }
    return ans;
}
posted @ 2023-04-08 11:54  徐子洋  阅读(28)  评论(0)    收藏  举报  来源