1059E】Split the Tree
@題目描述 - [email protected]
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 這顆子樹按題意剖分得到的最小鏈條數,再令 。不難發現 a[i] 有上界: 。上式表示將 i 單獨剖分成一條鏈的鏈數。再進一步思考,a[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);
}
@[email protected]
就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。