【题目】
原题地址
一棵 n n n个节点的有根树,每个节点有权值 w i w_i wi。有两种操作:
【解题思路】
正着做似乎不是很好做。
考虑倒着进行这个过程,我们每次在 i i i上放 w i w_i wi个石子,或者在 i i i所有孩子都有石子时取走 i i i的石子,初始树中一个点上有石子,目标是清空树上所有石子,最小化历史最值。
观察后我们可以发现,一个点 i i i的所有孩子 j j j应该同时放上石子,接着立刻取走 i i i上的石子。
这个东西我们可以用类似线段树历史最值的方式来维护记录信息。
这个过程我们可以用二元组 ( − w i + ∑ w j , ∑ w j ) (-w_i+\sum w_j,\sum w_j) (−wi+∑wj,∑wj)来表示,意义为这个过程结束后的增量和过程中历史最大值与开始时石子数的差。
接下来我们定义 ( x , y ) (x,y) (x,y)的优先级:
我们找出最优的一个过程,若它父亲已经完成过程,则立即执行这个过程;否则在其父亲过程完成后立即执行这个过程,那么我们需要将两个二元组合并: ( a , b ) + ( c , d ) = ( a + c , max ( b , a + d ) ) (a,b)+(c,d)=(a+c,\max (b,a+d)) (a,b)+(c,d)=(a+c,max(b,a+d))。
寻找最优的二元组我们用一个优先队列或者 set \text{set} set维护即可。
不(Y)难(Y)看(yi)出(xia),每个子树中的选择顺序是全局的子序列,于是我们对全局做一次这个过程,将全局序列求出来,然后线段树合并就可以了。
【参考代码】
#include
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,fa[N],w[N];
ll ans[N],wson[N];
vector<int>son[N];
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x)
{
if(x>9) write(x/10);
putchar(x%10^48);
}
void writesp(ll x){write(x);putchar(' ');}
}
using namespace IO;
namespace Order
{
int ind,head[N],nex[N],pos[N],f[N];
struct data
{
ll sum,mx;int id;
data(ll _sum=0,ll _mx=0,int _id=0):sum(_sum),mx(_mx),id(_id){}
bool operator <(const data&x)const
{
if((sum<0)^(x.sum<0)) return sum<x.sum;
else if(sum>=0) return mx-sum==x.mx-x.sum?id<x.id:mx-sum>x.mx-x.sum;
return mx==x.mx?id<x.id:mx<x.mx;
}
}a[N];
set<data>st;
int findf(int x){return f[x]==x?x:f[x]=findf(f[x]);}
void datamerge(int x,int y)
{
nex[head[y]]=x;head[y]=head[x];f[x]=y;
if(st.find(a[y])==st.end()) return;
st.erase(a[y]);
a[y].sum+=a[x].sum;a[y].mx=max(a[x].mx,a[x].sum+a[y].mx);
st.insert(a[y]);
}
void getorder()
{
for(int i=1;i<=n;++i) head[i]=f[i]=i;
for(int i=1;i<=n;++i)
{
data tmp=*st.rbegin();st.erase(tmp);
datamerge(tmp.id,findf(fa[tmp.id]));
}
for(int i=1,ind=n;i;i=nex[i]) pos[i]=ind--;
}
}
using namespace Order;
namespace Segment
{
int rt[N];
struct node
{
ll sum,mx;
int ls,rs;
};
struct Segment
{
node t[N*100];int sz;
void pushup(int x)
{
t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum;
t[x].mx=max(t[t[x].ls].mx,t[t[x].ls].sum+t[t[x].rs].mx);
}
void update(int &x,int l,int r,int p,ll sum,ll mx)
{
if(!x) x=++sz;
if(l==r){t[x].sum=sum;t[x].mx=mx;return;}
int mid=(l+r)>>1;
if(p<=mid) update(t[x].ls,l,mid,p,sum,mx);
else update(t[x].rs,mid+1,r,p,sum,mx);
pushup(x);
}
int merge(int x,int y)
{
if(!x || !y) return x|y;
t[x].ls=merge(t[x].ls,t[y].ls);
t[x].rs=merge(t[x].rs,t[y].rs);
pushup(x);return x;
}
}tr;
void dfs(int x)
{
tr.update(rt[x],1,n,pos[x],w[x]-wson[x],w[x]);
for(int i=0;i<(int)son[x].size();++i)
{
int v=son[x][i];
dfs(v);rt[x]=tr.merge(rt[x],rt[v]);
}
ans[x]=tr.t[rt[x]].mx;
}
void getans()
{
dfs(1);
for(int i=1;i<=n;++i) writesp(ans[i]);
}
}
using namespace Segment;
void init()
{
read();n=read();
for(int i=2;i<=n;++i) fa[i]=read(),son[fa[i]].pb(i);
for(int i=1;i<=n;++i) w[i]=read(),wson[fa[i]]+=w[i];
for(int i=1;i<=n;++i) a[i]=data(w[i]-wson[i],w[i],i),st.insert(a[i]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("UOJ418.in","r",stdin);
freopen("UOJ418.out","w",stdout);
#endif
init();getorder();getans();
return 0;
}