1. 程式人生 > >1059E Split the Tree(貪心+樹上倍增)

1059E Split the Tree(貪心+樹上倍增)

E. Split the Tree

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given a rooted tree on $$$n$$$ vertices, its root is the vertex number $$$1$$$. The $$$i$$$-th vertex contains a number $$$w_i$$$. 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 $$$w_i$$$ on each path does not exceed $$$S$$$. Each vertex should belong to exactly one path.

A vertical path is a sequence of vertices $$$v_1, v_2, \ldots, v_k$$$ where $$$v_i$$$ ($$$i \ge 2$$$) is the parent of $$$v_{i - 1}$$$.

Input

The first line contains three integers $$$n$$$, $$$L$$$, $$$S$$$ ($$$1 \le n \le 10^5$$$, $$$1 \le L \le 10^5$$$, $$$1 \le S \le 10^{18}$$$) — 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 $$$w_1, w_2, \ldots, w_n$$$ ($$$1 \le w_i \le 10^9$$$) — the numbers in the vertices of the tree.

The third line contains $$$n - 1$$$ integers $$$p_2, \ldots, p_n$$$ ($$$1 \le p_i < i$$$), where $$$p_i$$$ 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.

題意:給你一棵樹,問你最多能把這棵樹分成多少條鏈,使得每條鏈的長度不超過L,每條鏈上的點的權值和不超過S。這裡的鏈,是不能跨過子樹的根節點的鏈(樹鏈剖分那樣子的鏈)。

解題思路:很容易想到貪心,但是從上往下貪心不可取,但是我們知道每一個葉子節點必定屬於一條鏈,那麼那個葉子節點所在的鏈必定是越長越好。所以我們可以從下往上貪心,每個節點儘可能的走到他的最遠的能走到的父親那裡,這裡我們可以用樹上倍增處理。我們用top記錄每個節點最長能去到哪裡,然後從下往上遍歷每一個節點,每個節點貪心的取能去到的最遠的節點即可。整個過程很像樹鏈剖分,詳見程式碼。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=100005;
typedef long long ll;

vector<int> G[MAXN];
int fa[25][MAXN];
int dep[MAXN];
int top[MAXN];//每個節點最遠能去到的那個點
int W[MAXN];
ll sum[MAXN];//從上往下走的節點值的和
int son[MAXN];//每個節點取的那個最遠能走到的那個點
ll N,L,S;

//倍增處理節點的第2^k個祖先是哪個
void initST(){
    fa[0][1]=-1;
    for(int k=0;k<20;k++)
        for(int i=1;i<=N;i++)
            if(fa[k][i]<0)
                fa[k+1][i]=-1;
            else
                fa[k+1][i]=fa[k][fa[k][i]];
}

//求出top,sum陣列
void dfs1(int u,int pre){
    sum[u]=sum[pre]+W[u];
    dep[u]=dep[pre]+1;
    top[u]=u;
    //倍增找能去到的最遠的點
    int dis=L;
    for(int k=20;k>=0;k--){
        int f=fa[k][top[u]];
        if((1<<k)>=dis||f==-1)//判斷L是否滿足
            continue;
        if(sum[u]-sum[f]+W[f]>S)//判斷S是否滿足
            continue;
        dis-=(1<<k);
        top[u]=f;//更新
    }
    for(int i=0;i<G[u].size();i++)
        dfs1(G[u][i],u);
}

//求出son陣列並記錄答案
int ans=0;
void dfs2(int u){
    int best=-1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        dfs2(v);
        if(son[v]==v)//這個很重要,相當於把已經確定的鏈都刪掉
            continue;
        if(best==-1||dep[best]>dep[son[v]])//儲存最遠的那個
            best=son[v];
    }
    if(best==-1){
        best=top[u];
        ans++;
    }
    son[u]=best;
}

int main(){
    
    scanf("%lld%lld%lld",&N,&L,&S);
    for(int i=1;i<=N;i++){
        scanf("%lld",&W[i]);
        if(W[i]>S)
        {
            cout<<-1<<endl;
            return 0;
        }
    }
    int tmp;
    for(int i=2;i<=N;i++)
    {
        scanf("%d",&tmp);
        fa[0][i]=tmp;
        G[tmp].push_back(i);
    }
    
    initST();
    dfs1(1,0);
    dfs2(1);
    cout<<ans<<endl;
    return 0;
}