Niyaz and Small Degrees / 封闭道路
题目大意
给一棵树,边有边权,是砍掉的费用。
对于每个 x,询问最少要花费费用砍边,使得每个点都不与超过 x 个点相连。
思路
首先看到题目容易想到要 DP。
那我们就想想 n2logn 的 DP,我们枚举 x,然后搞 O(nlogn) DP。
那就是设 fi,0/1 为 i 与父亲连边删或不删时以 i 为根的子树的点都满足度数不大于 x 的最小费用。
在度数没有限制的时候,那对于 u 的儿子 v,之间边权为 w,有 fu,0=fu,1=min{fv,0,fv,1+w}
那如果有了度数限制,那我们就要想,你选左边就是割,右边就是不割。
那对于 fu,0,它就一定要选 duu−x 个左边的。(duu 为 u 的度数)
那对于 fu,1,它就一定要选 duu−x−1 个左边的。
那容易想到,如果本来就是左边优,那我们肯定选左边。那我们可以提前选,然后记得要选的个数要减。
那如果是右边优,那我们就看如果硬要选左边会亏多少(fv,1+w−fv,0),那我们肯定优先硬改亏的最少的,那我们可以用堆来找到前 duu−x 个的总费用。
那就可以了。
那我们考虑怎么优化。
不难想到,如果 x≥duu,那 u 这个点割不割都无所谓,那我们可以把这个点删掉。
但不能把别的点入度修改,而且别的点就相当于有一个费用是 w(w 是它与这个点的边的边权)放进了堆里面。而且这个是一只存在的,不像我们上面算法的堆,那些要清空,而这些要留下。
那我们就要弄一个可删堆,可以用两个普通的堆来维护。
(大概就是你要删你就不直接删而是放进删除堆,每次你要取之前如果你的普通堆和删除堆堆顶相同就把它弹出不要,即我们不立刻删掉,而是阻碍到再删)
(具体是看看代码)
那我们接着继续想,那每次枚举 x,DP 的规模就不是 nlogn,而是 mlogm(m 时度数超过 x 的点数)
那这个所有 m 加起来的规模其实是 O(n) 级别的,因为你想,入度和是 2n,那一个点入度是 x 就会贡献 x 次,那总共就最多贡献 2n 次,所以 m 就是 O(n) 级别。
复杂度也就变成了 O(nlogn)
具体的实现可以看看代码。
代码
由于两道题是一模一样的,而且 APIO 由于那个交互代码会有点乱,这里就只在 CF 的那个上面写注释。
然后记得两个的点编号一个是 1∼n,一个是 0∼n−1。
Niyaz and Small Degrees
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
int n, x, y, z, du[250001], xx[250001];
int nd, kil, in[250001];
vector <pair<int, int> > e[250001];
ll sum, f[250001][2], re;
vector <ll> ans, tmp, del;
struct cd_heap {
priority_queue <int> q1, q2;
ll sum;
void insert(int x) {q1.push(x); sum += x;}
void delete_(int x) {q2.push(x); sum -= x;}
void ck() {while (!q1.empty() && !q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();}
int top() {ck(); return q1.top();}
void pop() {ck(); sum -= q1.top(); q1.pop();}
int size() {return q1.size() - q2.size();}
}h[250001];
void add(int x, int y, int z) {
e[x].push_back(make_pair(y, z));
du[x]++;
}
bool cmp0(pair <int, int> x, pair <int, int> y) {
return du[x.first] > du[y.first];
}
bool cmp(int x, int y) {
return du[x] < du[y];
}
void kill(int x) {
for (int i = 0; i < e[x].size(); i++){
int y = e[x][i].first, z = e[x][i].second;
if (du[y] <= nd) break;
h[y].insert(z);
}
}
void dfs(int now, int father) {
in[now] = nd;
int num = du[now] - nd;
while (h[now].size() > num)
h[now].pop();
for (int i = 0; i < e[now].size(); i++) {
int to = e[now][i].first;
if (du[to] <= nd) break;
if (to == father) continue;
dfs(to, now);
}
tmp.clear(); del.clear();
for (int i = 0; i < e[now].size(); i++) {
int to = e[now][i].first, dis = e[now][i].second;
if (du[to] <= nd) break;
if (to == father) continue;
ll x = f[to][1] + dis - f[to][0];
if (x <= 0) {
re += f[to][1] + dis;
num--;
}
else {
re += f[to][0];
del.push_back(x);
h[now].insert(x);
}
}
while (h[now].size() && h[now].size() > num)
tmp.push_back(h[now].top()), h[now].pop();
f[now][0] = h[now].sum;
while (h[now].size() && h[now].size() > num - 1)
tmp.push_back(h[now].top()), h[now].pop();
f[now][1] = h[now].sum;
for (int i = 0; i < tmp.size(); i++)
h[now].insert(tmp[i]);
for (int i = 0; i < del.size(); i++)
h[now].delete_(del[i]);
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
sum += z;
}
ans.push_back(sum);
for (int i = 1; i <= n; i++) {
sort(e[i].begin(), e[i].end(), cmp0);
xx[i] = i;
}
sort(xx + 1, xx + n + 1, cmp);
kil = 1;
while (++nd < n) {
while (kil <= n && nd == du[xx[kil]]) kill(xx[kil]), kil++;
if (kil > n) {
ans.push_back(0);
continue;
}
re = 0;
for (int i = kil; i <= n; i++) {
if (in[xx[i]] == nd) continue;
dfs(xx[i], 0);
re += f[xx[i]][0];
}
ans.push_back(re);
}
for (int i = 0; i < ans.size(); i++)
printf("%lld ", ans[i]);
return 0;
}
封闭道路
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
int n, x, y, z, du[250001], xx[250001];
int nd, kil, in[250001];
vector <pair<int, int> > e[250001];
ll sum, f[250001][2], re;
vector <ll> ans, tmp, del;
struct cd_heap {
priority_queue <int> q1, q2;
ll sum;
void insert(int x) {q1.push(x); sum += x;}
void delete_(int x) {q2.push(x); sum -= x;}
void ck() {while (!q1.empty() && !q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();}
int top() {ck(); return q1.top();}
void pop() {ck(); sum -= q1.top(); q1.pop();}
int size() {return q1.size() - q2.size();}
}h[250001];
void add(int x, int y, int z) {
e[x].push_back(make_pair(y, z));
du[x]++;
}
bool cmp0(pair <int, int> x, pair <int, int> y) {
return du[x.first] > du[y.first];
}
bool cmp(int x, int y) {
return du[x] < du[y];
}
void kill(int x) {
for (int i = 0; i < e[x].size(); i++){
int y = e[x][i].first, z = e[x][i].second;
if (du[y] <= nd) break;
h[y].insert(z);
}
}
void dfs(int now, int father) {
in[now] = nd;
int num = du[now] - nd;
while (h[now].size() > num)
h[now].pop();
for (int i = 0; i < e[now].size(); i++) {
int to = e[now][i].first;
if (du[to] <= nd) break;
if (to == father) continue;
dfs(to, now);
}
tmp.clear(); del.clear();
for (int i = 0; i < e[now].size(); i++) {
int to = e[now][i].first, dis = e[now][i].second;
if (du[to] <= nd) break;
if (to == father) continue;
ll x = f[to][1] + dis - f[to][0];
if (x <= 0) {
re += f[to][1] + dis;
num--;
}
else {
re += f[to][0];
del.push_back(x);
h[now].insert(x);
}
}
while (h[now].size() && h[now].size() > num)
tmp.push_back(h[now].top()), h[now].pop();
f[now][0] = h[now].sum;
while (h[now].size() && h[now].size() > num - 1)
tmp.push_back(h[now].top()), h[now].pop();
f[now][1] = h[now].sum;
for (int i = 0; i < tmp.size(); i++)
h[now].insert(tmp[i]);
for (int i = 0; i < del.size(); i++)
h[now].delete_(del[i]);
}
std::vector<long long> minimum_closure_costs(int N, std::vector<int> U,
std::vector<int> V,
std::vector<int> W) {
n = N;
for (int i = 1; i < n; i++) {
x = U[i - 1] + 1;
y = V[i - 1] + 1;
z = W[i - 1];
add(x, y, z);
add(y, x, z);
sum += z;
}
ans.push_back(sum);
for (int i = 1; i <= n; i++) {
sort(e[i].begin(), e[i].end(), cmp0);
xx[i] = i;
}
sort(xx + 1, xx + n + 1, cmp);
kil = 1;
while (++nd < n) {
while (kil <= n && nd == du[xx[kil]]) kill(xx[kil]), kil++;
if (kil > n) {
ans.push_back(0);
continue;
}
re = 0;
for (int i = kil; i <= n; i++) {
if (in[xx[i]] == nd) continue;
dfs(xx[i], 0);
re += f[xx[i]][0];
}
ans.push_back(re);
}
return ans;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现