http://codeforces.com/problemset/problem/1059/E
给一棵树,每个点有一些点权。定义垂直路径为:一条树上路径 ,对于 都有 为 的父亲。
现在要把这棵树划分成若干条垂直路径,要求每条垂直路径的点个数不超过 ,点权和不超过 ,使每个节点都被一条垂直路径包含,问最少能用多少条路径划分。
题解
好像贪心就行了啊,计算每颗子树的最优解时,根节点可以是新开一条路径,或者使接上一条经过根节点的路径,并且这条路径向上延伸的长度是最长的。正确性。。。显然?
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int rint() {
int n, c, sgn = 0;
while ((c = getchar()) < '-');
if (c == '-') n = 0, sgn = 1;
else n = c - '0';
while ((c = getchar()) >= '0') n = 10 * n + c - '0';
return sgn ? -n : n;
}
const int N = 100010;
int n;
int L;
ll S;
int par[N], gp[N][20];
ll gw[N][20];
int w[N];
int up[N];
int top[N];
int dep[N];
int main() {
scanf("%d %d %lld", &n, &L, &S);
for (int i = 0; i < n; i++) {
scanf("%d", &w[i]);
if (w[i] > S) {
puts("-1");
return 0;
}
}
par[0] = -1;
for (int i = 1; i < n; i++) {
scanf("%d", &par[i]);
par[i]--;
}
for (int i = 0; i < n; i++) {
int p = par[i];
gp[i][0] = p;
if (p != -1) {
gw[i][0] = w[p];
dep[i] = dep[p] + 1;
} else {
gw[i][0] = 0;
dep[i] = 0;
}
for (int j = 0; j < 19; j++) {
if (gp[i][j] == -1) {
gp[i][j+1] = -1;
} else {
gp[i][j+1] = gp[gp[i][j]][j];
gw[i][j+1] = gw[i][j] + gw[gp[i][j]][j];
}
}
int A = 1;
ll B = w[i];
int x = i;
for (int j = 19; j >= 0; j--) {
if (gp[x][j] == -1) continue;
if (A + (1 << j) <= L && B + gw[x][j] <= S) {
A += (1 << j);
B += gw[x][j];
x = gp[x][j];
}
}
assert(x >= 0);
up[i] = x;
}
int ans = 0;
fill(top, top + n, -1);
for (int i = n-1; i >= 0; i--) {
if (top[i] == -1) {
top[i] = up[i];
ans++;
}
int p = par[i];
if (dep[top[i]] <= dep[p] && (top[p] == -1 || dep[top[i]] < dep[top[p]])) {
top[p] = top[i];
}
}
printf("%d\n", ans);
return 0;
}
总结
这种用 来表示树的方法好评啊,不用递归了。
网友评论