文章目录
- 题目
- 分析
- 代码
题目
[CodeForces 1119F] Niyaz and Small Degrees
分析
先考虑对于一个给定的
DP:
表示
结点的度数为
,且其子树合法的删边代价。
就意味着
与其父亲的边保留,
意味着
与其父亲的边删除。
设
是
的儿子,
是
的边权,由于要求是度数 小于等于
,所以贪心地想
时肯定要删掉边
,因为删掉不仅花费更少,还能让度数减少,因此此时
否则我们先暂时保留该边:
做完一遍后可能点
的度数还不符合要求,这时候就要将之前“暂时保留”的边删掉,于是删掉边
的代价由
变成了
,变化量是
。因此将所有暂时保留的边的
放进一个优先队列中,取最小的
个加进
即可(
是还要删的边数)。
注意到原图中度数小于等于 的点实际上可以删掉。设某个度数小于等于 的点为 ,它的某个度数大于 的邻接点为 , 对于 来说就变成了一个叶子结点,于是只需要将边 的权 加入 的优先队列中, 就可以被删掉了。进而我们从小到大枚举 的话, 从此就彻底没用了。
按这个思路每次更新一下各个点的优先队列,再暴力 DP 一次,分析一下时间复杂度( 表示 号点的度)
代码
注意优先队列要支持删除,因为每次完了过后需要还原。
神仙卡常? 注意注释那里的两行不加会 TLE。
#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>#define RI register inttypedef long long LL;
typedef std::pair<int, int> PII;const int MAXN = 250000;int N, Lim;
int Deg[MAXN + 5];
std::vector<PII> P;
std::vector<PII> G[MAXN + 5];bool Comp(PII i, PII j) {return Deg[i.first] > Deg[j.first];
}struct DeletableQueue {LL Sum; int Size;std::priority_queue<LL> Q, D;inline void Push(const LL &x) { Q.push(x), Size++, Sum += x; }inline void Delete(const LL &x) { D.push(x), Size--, Sum -= x; }inline LL Top() { while (!Q.empty() && !D.empty() && Q.top() == D.top()) Q.pop(), D.pop(); return Q.top(); }inline void Pop() { Sum -= Top(); Q.pop(); Size--; }
}H[MAXN + 5];int Vis[MAXN + 5];
LL Dp[MAXN + 5][2];void Dfs(int u, int fa) {Vis[u] = Lim;int cut = Deg[u] - Lim;Dp[u][0] = Dp[u][1] = 0;// 不加下面两句会 T 飞while (H[u].Size > cut)H[u].Pop();std::vector<LL> Add, Del;for (RI i = 0; i < int(G[u].size()); i++) {int v = G[u][i].first, w = G[u][i].second;if (Deg[v] <= Lim) break;if (v != fa) {Dfs(v, u);LL tmp = Dp[v][1] + w - Dp[v][0];if (tmp <= 0) {Dp[u][0] += Dp[v][1] + w;Dp[u][1] += Dp[v][1] + w;cut--;}else {Dp[u][0] += Dp[v][0];Dp[u][1] += Dp[v][0];H[u].Push(tmp);Del.push_back(tmp);}}}if (cut > 0) {while (H[u].Size > cut)Add.push_back(H[u].Top()), H[u].Pop();Dp[u][0] += H[u].Sum;while (H[u].Size > cut - 1)Add.push_back(H[u].Top()), H[u].Pop();Dp[u][1] += H[u].Sum;}for (RI i = 0; i < int(Add.size()); i++)H[u].Push(Add[i]);for (RI i = 0; i < int(Del.size()); i++)H[u].Delete(Del[i]);
}int main() {scanf("%d", &N);LL Ans = 0;for (RI i = 1; i < N; i++) {int u, v, w; scanf("%d%d%d", &u, &v, &w);Deg[u]++, Deg[v]++;G[u].push_back(std::make_pair(v, w));G[v].push_back(std::make_pair(u, w));Ans += w;}printf("%lld", Ans);for (RI i = 1; i <= N; i++) {P.push_back(std::make_pair(Deg[i], i));std::sort(G[i].begin(), G[i].end(), Comp);}std::sort(P.begin(), P.end());RI cur = 0;for (Lim = 1; Lim < N; Lim++) {while (cur < N && P[cur].first == Lim) {int u = P[cur].second;for (RI i = 0; i < int(G[u].size()); i++) {int v = G[u][i].first, w = G[u][i].second;if (Deg[v] <= Lim) break;H[v].Push(w);}cur++;}Ans = 0;for (RI i = cur; i < N; i++) {int u = P[i].second;if (Vis[u] != Lim) {Dfs(u, 0);Ans += Dp[u][0];}}printf(" %lld", Ans);}return 0;
}