洛谷传送门
BZOJ传送门
题目描述
给定一棵 N N N个节点的有根树,根节点为 1 1 1。 Q Q Q次询问,每次给定一个 K K K,用最少的操作次数遍历完整棵树,输出最少操作次数。每次操作可以选择访问不超过 K K K个未访问的点,且这些点的父亲必须在之前被访问过。
输入输出格式
输入格式
第一行两个正整数 N , Q N, Q N,Q。
第二行 Q Q Q个正整数, 分别表示每组询问中的 K K K。
第三行 N − 1 N-1 N−1个正整数, 第 i i i个正整数表示 i + 1 i+1 i+1号节点的父节点。
输出格式
输出一行 Q Q Q个正整数, 表示对于每组询问最小的操作次数。
输入输出样例
输入样例#1:
20 1
3
1 1 1 3 4 3 2 8 6 9 10 12 12 13 14 11 11 11 11
输出样例#1:
8
解题分析
实在太菜了, 想不到 d p dp dp方程…
这个方程是这样的:
设
d
p
[
i
]
dp[i]
dp[i]表示每次最多选
i
i
i个节点,
s
u
m
[
i
]
sum[i]
sum[i]表示深度
≥
i
\ge i
≥i的节点总数, 就有:
d
p
[
i
]
=
m
a
x
(
j
+
⌈
s
u
m
[
j
+
1
]
i
⌉
)
dp[i]=max(j+\lceil\frac{sum[j+1]}{i}\rceil)
dp[i]=max(j+⌈isum[j+1]⌉)
大概意思就是前
j
j
j次取完前
j
j
j层, 后面每次都保证取满
i
i
i个节点。
为什么要取 m a x max max?因为有不合法的情况。
-
前 j j j次取不完, 那么就有 k < j k<j k<j满足前 k k k次是取完的, 容易证明此时不会比我们假设的不合法的 j j j次取完 j j j层更优。
-
后面取不满, 那么就有 k > j k>j k>j满足后面是取完的, 同样可得此时不会比这种不合法的情况更优。
然后如果有一个
k
>
j
k>j
k>j满足
j
+
⌈
s
u
m
[
j
+
1
]
i
⌉
<
k
+
⌈
s
u
m
[
k
+
1
]
i
⌉
j+\lceil\frac{sum[j+1]}{i}\rceil<k+\lceil\frac{sum[k+1]}{i}\rceil
j+⌈isum[j+1]⌉<k+⌈isum[k+1]⌉
就有
i
j
+
s
u
m
[
j
+
1
]
<
i
k
+
s
u
m
[
k
+
1
]
ij+sum[j+1]<ik+sum[k+1]
ij+sum[j+1]<ik+sum[k+1]
移项可得
i
(
k
−
j
)
>
s
u
m
[
j
+
1
]
−
s
u
m
[
k
+
1
]
i
>
s
u
m
[
j
+
1
]
−
s
u
m
[
k
+
1
]
k
−
j
i(k-j)>sum[j+1]-sum[k+1] \\ i>\frac{sum[j+1]-sum[k+1]}{k-j}
i(k−j)>sum[j+1]−sum[k+1]i>k−jsum[j+1]−sum[k+1]
很简单的斜率优化。
注意这里所有的 j , k j,k j,k的取值范围都是 [ 1 , m a x d e p ] [1,maxdep] [1,maxdep], 所以先插入完, 最后挨个查询即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <vector>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define db double
#define MX 1000050
#define ll long long
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int n, q, head = 0, tail = -1, dpmx, qmx;
std::vector <int> nex[MX];
int s[MX], dp[MX], que[MX], dep[MX], ask[MX];
IN db slope(R int ini, R int tar) {return 1.0 * (s[ini + 1] - s[tar + 1]) / (tar - ini);}
IN void insert(R int id)
{
W (head < tail && slope(que[tail - 1], que[tail]) >= slope(que[tail], id)) --tail;
que[++tail] = id;
}
void DFS(R int now)
{
dpmx = std::max(dpmx, dep[now]);
++s[dep[now]];
for (auto i : nex[now])
{
dep[i] = dep[now] + 1;
DFS(i);
}
}
int main(void)
{
in(n), in(q); int foo;
for (R int i = 1; i <= q; ++i)
in(ask[i]), qmx = std::max(qmx, ask[i]);
for (R int i = 2; i <= n; ++i)
in(foo), nex[foo].push_back(i);
dep[1] = 1; DFS(1);
for (R int i = dpmx - 1; ~i; --i) s[i] += s[i + 1];
for (R int i = 0; i < dpmx; ++i) insert(i);
for (R int i = 1; i <= qmx; ++i)
{
W (head < tail && slope(que[head], que[head + 1]) < i) ++head;
dp[i] = que[head] + (s[que[head] + 1] + i - 1) / i;
}
for (R int i = 1; i <= q; ++i) printf("%d ", dp[ask[i]]);
}