标签:二维 getc etc i++ ons col dep logs sdi
给定一棵以 1 为根的树.
多组询问, 每次给定 K , 每次选择不超过 K 个已被删除的节点删除, 问最少多少次才能清空.
n, K <= 1000000 .
神题.
考虑数形结合.
设 f[x] 为深度 >= x 的个数, f[x] 单调递减.
对二维平面上的点 (x, f[x]) 构建上凸壳.
依次预处理 1, 2, ..., H 的答案, 找到切线.
可以证明, 对于切点左边, 需要深度次, 对于切点右边, 每次可以删 K 个.
#include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> using namespace std; #define F(i, a, b) for (register int i = (a); i <= (b); i++) #define D(i, a, b) for (register int i = (a); i >= (b); i--) #define LL long long inline int rd(void) { int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1; int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f; } const int N = 1000005; int n, qs[N], par[N]; int dep[N], f[N], Max; int s[N], Len, ans[N]; inline LL det(int x, int y, int _x, int _y) { return 1LL * x * _y - 1LL * y * _x; } int main(void) { #ifndef ONLINE_JUDGE freopen("sup.in", "r", stdin); #endif n = rd(), qs[0] = rd(); F(i, 1, qs[0]) qs[i] = rd(); F(i, 2, n) par[i] = rd(); dep[1] = 1; F(i, 2, n) dep[i] = dep[par[i]] + 1; F(i, 1, n) f[dep[i]]++; D(i, n, 1) f[i] += f[i+1]; Max = *max_element(dep+1, dep+n+1); F(i, 1, Max+1) { while (Len >= 2 && det(i - s[Len-1], f[i] - f[s[Len-1]], s[Len] - s[Len-1], f[s[Len]] - f[s[Len-1]]) < 0) s[Len--] = 0; s[++Len] = i; } for (int K = 1, cur = 1; K <= n; K++) { while (cur+1 <= Len && det(1, -K, s[cur+1] - s[cur], f[s[cur+1]] - f[s[cur]]) >= 0) cur++; ans[K] = s[cur]-1 + (f[s[cur]]+K-1) / K; } F(i, 1, qs[0]) printf("%d ", qs[i] <= n ? ans[qs[i]] : Max); puts(""); return 0; }
标签:二维 getc etc i++ ons col dep logs sdi
原文地址:http://www.cnblogs.com/Sdchr/p/7527188.html