1106 字
6 分钟

P3571 [POI2014] - Supercomputer 题解

2024-08-25
2024-08-25
浏览量 加载中...
Done

您已获得最佳的阅读体验!

集训时讲到了这个题,刚好写篇题解记录一下思路。

题目地址:P3571

题目难度:NOI/NOI+/CTSC

给定一棵 nn 个节点的有根树,根节点为 11qq 次询问,每次给定一个 kk,用最少的操作次数遍历完整棵树,输出最少操作次数。每次操作可以选择访问不超过 kk 个未访问的点,且这些点的父亲必须在这次操作之前被访问过。

n,q106n, q \leq {10^6}

这道题其实有一个裸结论可以套:

对于一棵树,一定存在一个最优解,满足:恰好用 ii 步操作删除完树上深度小于等于 ii 的节点,此后一定可以通过每次删除 kk 个节点把整棵树的所有节点删除完毕。

形式化地,令 f(k)f(k) 为对于给定的 kk 所能达到的最小操作数,sis_i 为深度大于 ii 的节点总数。那么有:

f(k)=maxiTree{i+sik}f(k)=\max\limits_{i\in Tree}\{i+\lceil\frac{s_i}{k}\rceil\}

此时把它看作动态规划的转移方程,移项可以得到 si=ki+kf(k)s_i=-ki+kf(k),注意到自变量和因变量具有单调性,可以进行斜率优化,能够把时间复杂度降到 O(n)\mathcal O(n)

重点在于如何证明转移方程的正确性。要想证明一个相等关系,可以从 ABA\leq B 以及 ABA\geq B 两个方面分别入手证明,只要两个条件均满足,则代表 A=BA=B 成立。


首先来证 f(k)max{i+sik}f(k)\geq\max\{i+\lceil\frac{s_i}{k}\rceil\}:对于一个深度恰等于 ii 的节点,至少需要 ii 次操作,于是这是正确的。

再来证 f(k)max{i+sik}f(k)\leq\max\{i+\lceil\frac{s_i}{k}\rceil\}:假设上式在 i=ai=a 时取得最大值,并令 bb,假设前 bb 步可删去前 bb 层所有点,且它是满足该性质的最大的数。我们需要做的就是证明 a=ba=b

同样的思路,先证 aba\geq b。考虑反证法,对于 c<bc<b,若 i=ci=c 时的 f(k)f(k) 大于 i=bi=b 时的 f(k)f(k)。那么:

c+sck>b+sbkscsb>k(bc)\begin{aligned} c+\frac{s_c}{k}&>b+\frac{s_b}{k} \\s_c-s_b&>k(b-c) \end{aligned}

而不等号左侧用两个前缀和相减代表所有深度介于 c+1bc+1\sim b 之间的点的总数,删完所有 c+1bc+1\sim b 层的节点的操作步数应该大于 bcb-c,又因为删除一个第 cc 层的节点需要 cc 步,那么前 bb 层仅用 bb 步无法删除完毕。与假设中 bb 的意义矛盾,于是 aba\geq b 得证。

接下来证 aba\leq b:树的第 b+1b+1 层一定有超过 kk 个节点,有 f(k)b+1f(k)bf(k)_{b+1}\leq f(k)_b;对于 b+1b+1b+2b+2 两层,若这两层的节点总数不超过 2k{2k},那么 b+2b+2 层的节点数量一定小于 kk,于是可以用 b+2b+2 次操作删完前 b+2b+2 层的点,与 bb 是满足条件的最大的数矛盾,故 b+1b+1b+2b+2 两层节点总数一定大于 2k{2k}……以此类推,b+1b+nb+1\sim b+n 层一共有超过 nknk 个节点。此时一定有 aba\leq b 成立。

综上所述,a=ba=b。因为 b+1b+nb+1\sim b+n 层节点数一定大于 knkn,于是每次做删除 kk 个点的操作,并且优先删有孩子节点的节点即可。至此,整个结论得到证明。

最后就是套斜率优化的模板了(有现成的公式还真是方便呢):

#include <bits/stdc++.h>
#define N 1000010
using namespace std;
int dp[N];
int dep[N], s[N];
int query[N];
int q[N];
int hh = 0, tt = 0;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
int maxd = 0, maxq = 0;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> query[i];
maxq = max(maxq, query[i]);
}
s[1] = dep[1] = 1;
for (int i = 2; i <= n; i++) {
int x;
cin >> x;
dep[i] = dep[x] + 1; // 其实建单向边用DFS处理节点深度也是可以的,只是这道题并没这个必要?
s[dep[i]]++;
maxd = max(maxd, dep[i]);
}
for (int i = maxd; i; i--) s[i] += s[i + 1]; // 处理 i 层及更深层的节点总数
// 单调队列维护凸包
for (int i = 1; i <= maxd; i++) {
// 用交叉相乘法以免出题人卡精度
while (hh < tt && (s[q[tt] + 1] - s[i + 1]) * (q[tt] - q[tt - 1]) < (s[q[tt] + 1] - s[q[tt - 1] + 1]) * (q[tt] - i)) tt--;
q[++tt] = i;
}
for (int i = 1; i <= maxq; i++) {
while (hh < tt && (s[q[hh] + 1] - s[q[hh + 1] + 1]) < -i * (q[hh] - q[hh + 1])) hh++;
int j = q[hh];
dp[i] = j + (s[j + 1] + i - 1) / i; // 套转移方程
}
for (int i = 1; i <= m; i++) cout << dp[query[i]] << ' ';
cout << endl;
return 0;
}

The End\texttt{The End}

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
P3571 [POI2014] - Supercomputer 题解
https://justpureh2o.cn/articles/11186/
作者
JustPureH2O
发布于
2024-08-25
许可协议
CC BY-NC-SA 4.0
最后更新于 2024-08-25,距今已过 539 天

部分内容可能已过时

评论区

Profile Image of the Author
JustPureH2O
穷方圆平直之情,尽规矩准绳之用
公告
JustPureH2O 的博客现已正式迁移至 Astro!原 Hexo 网站将移至 https://hexo.justpureh2o.cn/
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
100
分类
12
标签
54
总字数
368,990
运行时长
0
最后活动
0 天前

目录