[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;
}