[CF1119F] Niyaz and Small Degrees
[题目链接]
https://codeforces.com/contest/1119/problem/F
[题解]
首先考虑一个朴素的做法。
设 \(dp_{u , 0 / 1}\) 表示以 \(u\) 为根的子树 , \((u , fa(u))\) 这条边割 / 不割的最小代价。
忽略 \(X\) 的限制 , 首先令 \(dp_{u , 0 / 1} = \sum_{v}{max\{dp_{v , 0} , dp_{v , 1} + w\}}\)。
不妨称 \(dp_{v , 1} + w \leq dp_{v , 0}\) 的儿子 \(v\) 叫 "好儿子" , 其余叫"坏儿子"。 那么要做的就是把 \(max\{0 , deg_{u} - cnt - X\}\) 个"坏儿子" 变为 "好儿子"。 把 \(dp_{v , 1} + w - dp_{v , 0}\) 放入一个堆中 , 取出前若干小的即可。
这样时间复杂度是 \(O(N^2logN)\) 的。
考虑优化 , 发现随着 \(X\) 增大 , 那些度数不超过 \(X\) 的点 \(u\) 就可以直接删去了 , 只需在与其相邻的点 \(v\) 的堆中加入 \(w(u , v)\) 即可。 然后再对剩下的点做一遍树形 \(DP\)。
因为 \(\sum{X}\sum_{i}{[deg_{i} > X]} = \sum{deg_{i}} = 2N - 2\) , 所以更新次数不超过 \(O(N)\)。
故时间复杂度 : \(O(NlogN)\)
[代码]
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair < int , int > pii;
#define mp make_pair
const int MN = 3e5 + 5;
int N , X;
vector < pii > E[MN];
int deg[MN] , vis[MN];
LL dp[MN][2];
vector < LL > del , tmp;
pii D[MN];
inline void AddEdge(int u , int v , int w) {
E[u].emplace_back(mp(v , w));
++deg[u];
}
inline bool cmp(pair < int , int > x , pair < int , int > y) {
return deg[x.first] > deg[y.first];
}
struct Heap {
int sz; LL sum;
priority_queue < LL > a , b;
inline void push(LL x) { a.push(x); ++sz; sum += (LL) x; }
inline void erase(LL x) { b.push(x); --sz; sum -= (LL) x; }
inline void check() { while (!a.empty() && !b.empty() && a.top() == b.top()) { a.pop(); b.pop(); } }
inline LL top() { check(); return a.top(); }
inline void pop() { check(); --sz; sum -= (LL) a.top(); a.pop(); }
inline int size() { return sz; }
} H[MN];
inline void die(int u) {
for (auto to : E[u]) {
int v = to.first , w = to.second;
if (deg[v] <= X) break;
H[v].push(w);
}
}
inline void dfs(int u , int fa = 0) {
vis[u] = X;
int need = deg[u] - X;
LL res = 0;
for (; H[u].size() > need; H[u].pop());
for (auto to : E[u]) {
int v = to.first , w = to.second;
if (deg[v] <= X) break; if (v == fa) continue;
dfs(v , u);
}
tmp.clear() , del.clear();
for (auto to : E[u]) {
int v = to.first , w = to.second; if (v == fa) continue;
if (deg[v] <= X) break;
LL x = dp[v][1] + w - dp[v][0];
if (x <= 0) { --need; res += dp[v][1] + w; continue; }
res += dp[v][0]; H[u].push(x); del.emplace_back(x);
}
for (; H[u].size() && H[u].size() > need; H[u].pop()) tmp.emplace_back(H[u].top());
dp[u][0] = res + H[u].sum;
for (; H[u].size() && H[u].size() > need - 1; H[u].pop()) tmp.emplace_back(H[u].top());
dp[u][1] = res + H[u].sum;
for (int i : tmp) H[u].push(i);
for (int i : del) H[u].erase(i);
}
int main() {
scanf("%d" , &N); LL sum = 0;
for (int i = 1; i < N; ++i) {
int u , v , w;
scanf("%d%d%d" , &u , &v , &w);
AddEdge(u , v , w); AddEdge(v , u , w);
sum += 1LL * w;
}
printf("%lld" , sum);
for (int i = 1; i <= N; ++i)
D[i] = mp(deg[i] , i) , sort(E[i].begin() , E[i].end() , cmp);
sort(D + 1 , D + N + 1);
int cur = 1;
for (X = 1; X < N; ++X) {
while (cur <= N && D[cur].first == X) die(D[cur].second) , ++cur;
LL ans = 0;
for (int j = cur; j <= N; ++j) {
int v = D[j].second;
if (vis[v] == X) continue;
dfs(v) , ans += 1LL * dp[v][0];
}
printf(" %lld" , ans);
}
printf("\n");
return 0;
}