4025: 魔炮(hz666666)

题目描述

 

住在很少人住的森林的普通的魔法使,雾雨魔理沙,在天空普通地飞着。

她发觉不知什么时候,森林边长出了一棵巨大的树。

想着那里一定有宝物的少女,为了去寻宝(?)而出发了。

魔理沙打算破坏这棵树来收集树里的宝物。这棵树可以表示成由nn个点组成,其中n−m+1n−m+1到nn号节点为叶子节点的一棵有根树。魔理沙已经知道了树的每个叶子节点的魔法值titi,她需要给剩下的每个节点确定一个魔法值,使得这棵树的相邻节点的最大魔法值差最小,这样她才能以最小的魔法消耗来破坏这棵树取得宝物。

 

输入

 

第一行包括两个整数nn和mm,表示树的节点数和叶子节点数。

接下来nn行,每行一个整数fifi,表示树上每个节点的父亲节点,输入保证f1=0f1=0。

接下来mm行,每行一个整数titi,表示第n−m+in−m+i个节点的魔法值。

 

输出

 

输出一个数,表示树的相邻节点的最大魔法值差的最小值。

 

样例输入

6 4
0
1
1
1
2
2
40
20
10
5

样例输出

12

提示

 

数据范围:1≤m≤n≤500000,0≤ti≤5000001≤m≤n≤500000,0≤ti≤500000

 

来源

hz

题解:

这道题的答案显然有单调性,所以我们用二分解决。

假设我们二分出一个mid,我们用类似树形dp的方法判断。

每一个点有一个l[i],r[i]表示魔法值的范围,然后一个节点的范围就是它的儿子范围的交集。

如果这个答案可行,那么每一个节点的l<=r,结束了。

#include
#include
#include
using namespace std;
int n,m,V[500005],nex[500005],las[500005],tot,a[500005],t,l[500005],r[500005],L,R;
int fa[500005];
void add(int t1,int t2)
{
    V[++tot]=t2;nex[tot]=las[t1];las[t1]=tot;
}
void dfs(int x,int mid)
{
    int h=las[x];
    while(h)
    {
        dfs(V[h],mid);
        h=nex[h];
    }
    if(l[x])return;
    if(l[x]==-1){l[fa[x]]=-1;return;}
    h=las[x];
    int tl=0,tr=1e9;
    while(h)
    {
        tl=max(tl,l[V[h]]-mid);tr=min(tr,r[V[h]]+mid);
        h=nex[h];
    }
    if(tl<=tr)l[x]=tl,r[x]=tr;
    else l[fa[x]]=l[x]=-1;
}
bool pd(int mid)
{
    for(int i=1;i<=n-m;++i)l[i]=r[i]=0;
    dfs(1,mid);
    for(int i=1;i<=n-m;++i)if(l[i]==-1)return false;
    return true;
}
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=2,t;i<=n;++i)scanf("%d",&t),add(t,i),fa[i]=t;
    for(int i=n-m+1;i<=n;++i)scanf("%d",&a[i]),l[i]=r[i]=a[i],R=max(R,a[i]);
    while(L>1;
        if(pd(mid))R=mid;
        else L=mid+1;
    }
    cout<

 

你可能感兴趣的:(4025: 魔炮(hz666666))