@Split the Tree@
@题目描述 - English@
time limit per test:2 seconds
memory limit per test:256 megabytes
You are given a rooted tree on n vertices, its root is the vertex number 1. The i-th vertex contains a number wi. Split it into the minimum possible number of vertical paths in such a way that each path contains no more than L vertices and the sum of integers wi on each path does not exceed S. Each vertex should belong to exactly one path.
A vertical path is a sequence of vertices v1,v2,…,vk where vi (i≥2) is the parent of vi−1.
Input
The first line contains three integers n, L, S (1≤n≤105,1≤L≤105, 1≤S≤1018) — the number of vertices, the maximum number of vertices in one path and the maximum sum in one path.
The second line contains n integers w1,w2,…,wn (1≤wi≤109) — the numbers in the vertices of the tree.
The third line contains n−1 integers p2,…,pn (1≤pi<i), where pi is the parent of the i-th vertex in the tree.
Output
Output one number — the minimum number of vertical paths. If it is impossible to split the tree, output −1.
Examples
input
3 1 3
1 2 3
1 1
output
3
input
3 3 6
1 2 3
1 1
output
2
input
1 1 10000
10001
output
-1
Note
In the first sample the tree is split into {1}, {2}, {3}.
In the second sample the tree is split into {1, 2}, {3} or {1, 3}, {2}.
In the third sample it is impossible to split the tree.
@题目翻译@
将一棵n个点的带权有根树剖分成尽量少的链,使得(1)链的两个端点是祖先关系(2)链含有的顶点个数小于等于L(3)链上所有点的点权和小于等于S。
求出最少链的数量,如果无解输出-1。N<=105。
@分析@
【当时比赛的时候我真的想到了正解来着……】
【只是自己带了一个“树上倍增永远写不对”的buff……所以没有A掉QAQ。】
【后来就去调D题。然后D题平方差公式推错……整场比赛就崩掉了QAQ……】
首先,如果所有的链都只包含一个结点,那么如果存在结点i使得它的点权wi>S则无解,否则就有解。
我们令 a[i] 表示将 i 这颗子树按题意剖分得到的最小链条数,再令 s u m [ i ] = ( ∑ j j 是 i 的 孩 子 a [ j ] ) sum[i] = (\sum_j^{j是i的孩子}a[j]) sum[i]=(∑jj是i的孩子a[j])。不难发现 a[i] 有上界: a [ i ] < = s u m [ i ] + 1 a[i]<=sum[i]+1 a[i]<=sum[i]+1。上式表示将 i 单独剖分成一条链的链数。再进一步思考,a[i] 也有下界: s u m [ i ] < = a [ i ] sum[i]<=a[i] sum[i]<=a[i]。上式表示增加一个结点过后,链的数量不会变少。
于是:
a
[
i
]
=
s
u
m
[
i
]
或
s
u
m
[
i
]
+
1
a[i]=sum[i]或sum[i]+1
a[i]=sum[i]或sum[i]+1。
【为简化描述,如果
a
[
i
]
=
s
u
m
[
i
]
a[i]=sum[i]
a[i]=sum[i]我们就称 i 为黑点,否则为灰点。】
如果结点 i 是黑点,则它不能够单独成链,那么它肯定与它的某一个孩子共链。所以有这样一个结论:最优策略下,链底端的结点 d 一定满足灰点。
再一步推导,就能得到一个关键的结论:在以结点 i 为根的子树中,如果存在灰点 d ,使得 i~d 是一条合法的链,则 i 为黑点。这个结论可以用归纳法证明(当然也可以猜测它是对的,跳过证明)。
然后,根据上面那个结论可知,灰点一定是在链的底端。所以有链的数量 = 灰点数量。
那么,现在开始描述算法:从根节点开始dfs。将所有的儿子都遍历完后,如果当前点 rt 为灰点,则沿着 rt 往上走,直到走到 p 使得 p ~ rt这条链不合法。然后 p ~ rt上的(除p,rt以外)点记为黑点;否则如果 rt 为黑点,不作任何操作。
找结点 p 可以用倍增法。标记 p~rt 上的点可以用树链剖分。
@代码@
啊啊啊……总觉得还是很可惜啊QAQ
考场上写的代码略丑,请大家见谅。如果大家还有什么疑问或者我的程序错了,可以评论在下面。
我会尽心回复大家的评论的qwq!
#include<cstdio>
typedef long long ll;
const int MAXN = 100000;
const ll INF = (1LL<<60);
struct edge{
int to;
edge *nxt;
}edges[MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p=(++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
}
int n, L; ll w[MAXN + 5], S;
int fa[MAXN + 5], dep[MAXN + 5], siz[MAXN + 5];
int fir[MAXN + 5], dfn[MAXN + 5], top[MAXN + 5], dcnt = 0;
void dfs1(int rt, int pre) {
fa[rt] = pre, dep[rt] = dep[pre] + 1, siz[rt] = 1;
for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
if( p->to == pre ) continue;
dfs1(p->to, rt);
siz[rt] += siz[p->to];
}
}
void dfs2(int rt, int tp) {
top[rt] = tp, fir[rt] = (++dcnt), dfn[dcnt] = rt;
int hvy = 0;
for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
if( p->to == fa[rt] ) continue;
if( hvy == 0 || siz[p->to] > siz[hvy] )
hvy = p->to;
}
if( !hvy ) return ;
dfs2(hvy, tp);
for(edge *p=adj[rt];p!=NULL;p=p->nxt)
if( p->to != fa[rt] && p->to != hvy )
dfs2(p->to, p->to);
}
struct node{
int le, ri;
bool tag;
}tree[4*MAXN + 5];
void Build(int x, int l, int r) {
tree[x].le = l, tree[x].ri = r;
if( l == r ) return ;
int Mid = (l + r) >> 1;
Build(x<<1, l, Mid);
Build(x<<1|1, Mid+1, r);
}
void Modify(int x, int l, int r) {
if( l > tree[x].ri || r < tree[x].le )
return ;
if( l <= tree[x].le && tree[x].ri <= r ) {
tree[x].tag = true;
return ;
}
if( tree[x].tag ) return ;
Modify(x<<1, l, r);
Modify(x<<1|1, l, r);
}
bool Query(int x, int pos) {
if( pos > tree[x].ri || pos < tree[x].le )
return false;
if( pos == tree[x].le && pos == tree[x].ri ) return tree[x].tag;
if( tree[x].tag ) return true;
else return Query(x<<1, pos) || Query(x<<1|1, pos);
}
void TModify(int u, int v) {
while( top[u] != top[v] ) {
if( dep[top[u]] > dep[top[v]] ) {
Modify(1, fir[top[u]], fir[u]);
u = fa[top[u]];
}
else {
Modify(1, fir[top[v]], fir[v]);
v = fa[top[v]];
}
}
if( dep[u] > dep[v] )
Modify(1, fir[v], fir[u]);
else Modify(1, fir[u], fir[v]);
}
int fa1[MAXN + 5][25];
ll sum[MAXN + 5][25];
void dfs(int rt) {
for(int i=1;i<20;i++) {
fa1[rt][i] = fa1[fa1[rt][i-1]][i-1];
if( sum[rt][i-1] != INF && sum[fa1[rt][i-1]][i-1] != INF )
sum[rt][i] = sum[rt][i-1] + sum[fa1[rt][i-1]][i-1];
else sum[rt][i] = INF;
}
for(edge *p=adj[rt];p!=NULL;p=p->nxt) {
fa1[p->to][0] = rt;
sum[p->to][0] = w[rt];
dfs(p->to);
}
}
int UpTo(int u, ll lim, int dep) {
dep--; lim -= w[u];
for(int i=19;i>=0;i--) {
if( sum[u][i] <= lim && dep >= (1<<i) ) {
lim -= sum[u][i];
dep -= (1<<i);
u = fa1[u][i];
}
}
return u;
}
int ans = 0;
void dfs3(int rt) {
for(edge *p=adj[rt];p!=NULL;p=p->nxt)
dfs3(p->to);
if( !Query(1, fir[rt]) ) {
TModify(rt, UpTo(rt, S, L));
ans++;
}
}
int main() {
scanf("%d%d%I64d", &n, &L, &S);
for(int i=1;i<=n;i++)
scanf("%I64d", &w[i]);
for(int i=2;i<=n;i++) {
int p;
scanf("%d", &p);
addedge(p, i);
}
for(int i=1;i<=n;i++) {
if( w[i] > S ) {
printf("%d\n", -1);
return 0;
}
}
for(int i=0;i<20;i++) {
sum[0][i] = (1LL<<60);
fa1[0][i] = 0;
}
sum[1][0] = (1LL<<60);
dfs1(1, 0); dfs2(1, 1); Build(1, 1, n); dfs(1); dfs3(1);
printf("%d\n", ans);
}
@END@
就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。