题意:
Snuke 有一棵 个点的有根树,每个点有权值 ,初始每个结点上都没有石子。
Snuke 准备了一些石子,并把它们拿在手中。她可以进行以下两种操作任意多次:
1 从手中取 个石子放在结点 上,进行该操作要求结点 的所有孩子 上都有 个石子。
2 将结点 上的所有石子收回手中。
Takahashi 想知道对于每个 ,为了在结点 上放 个石子,Snuke 至少需要准备多少石子。
解法:
如果知道石子的摆放顺序,就可以直接模拟了.然后考虑贪心确定石子的摆放顺序:
定义一个二元组(a,b),表示一次操作,其中a表示操作后带来的石子增量,b表示操作中出现的石子最大值.然后合并的时候(a,b)+(c,d)=(a+c,max(b,a+d)).
然后因为如果正着做,会有限制:儿子放完,父亲才能放,一个点会被很多点限制,不太行.所
以需要倒着做:这样问题转化成,有一个点有石子,可以每次去掉一个点的石子,然后把这
个点的儿子放满石子,目标是所有点都没有石子,问过程中的石子个数历史最大值最小可
以是多少.这样每个点只会被一个点限制.然后维护上述的二元组,贪心选最小的,
这里的贪心方法是:尝试交换相邻位置,看会不会使结果更优:总结下来就是
先做a<0的,a<0时,先做b大的.a>=0时,先做b-a小的
如果最小的暂时选不了,就把它和它的父亲合并在一起(表示选了父亲就立即选儿子),这样一直做,直到达成目标为止.这样就有了操作序列,然后模拟求答案的时候需要线段树合并.
这里可以发现某个子树的最优解也是全局的最优解的一部分,所以可以直接对所有这样的二元组排序出最优序列
需要线段树合并是因为要求对于每个点的答案,而每个点所含有的子树在最优序列中不一定是连续的一段.
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int T,n,fa[maxn],w[maxn],id[maxn];
vector<int> son[maxn];
int s[maxn],f[maxn];
inline int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
struct node{
int x,y,h,t;
}pre[maxn];
int val[maxn];
set<node> q;
node operator +(node a,node b){
return (node){a.x+b.x,max(a.y,a.x+b.y),a.h,b.t};
}
bool operator <(node a,node b){
int o1=a.x>=0,o2=b.x>=0;
if(o1!=o2)return a.x<b.x;
if(!o1){
if(a.y!=b.y)return a.y<b.y;
return a.h<b.h;
}
if(a.y-a.x!=b.y-b.x)return a.y-a.x>b.y-b.x;
return a.h<b.h;
}
int vis[maxn],nxt[maxn];
#define ls t[rt].l
#define rs t[rt].r
struct tree{
node x;int l,r;
}t[maxn*45];
int tot,ans[maxn],rt[maxn];
void ins(int &rt,int l,int r,int x,int id){
rt=++tot;
if(l==r){
t[rt].x=(node){val[id],s[id],l,l};
return ;
}
int mid=(l+r)>>1;
if(x<=mid)ins(ls,l,mid,x,id);
else ins(rs,mid+1,r,x,id);
t[rt].x=t[ls].x+t[rs].x;
}
int merge(int l,int r){
if((!l)||(!r))return l^r;
int alfa=++tot;
t[alfa]=t[l];
t[alfa].l=merge(t[l].l,t[r].l);
t[alfa].r=merge(t[l].r,t[r].r);
t[alfa].x=t[t[alfa].l].x+t[t[alfa].r].x;
return alfa;
}
void dfs(int u,int fa){
ins(rt[u],1,n,id[u],u);
for(vector<int>::iterator it=son[u].begin();it!=son[u].end();it++){
int v=*it;
dfs(v,u);
rt[u]=merge(rt[u],rt[v]);
}
ans[u]=t[rt[u]].x.y+w[u];
}
signed main(){
//freopen("uoj418.in","r",stdin);
//freopen("uoj418.out","w",stdout);
T=read();
n=read();
for(int i=2;i<=n;i++){
fa[i]=read();
son[fa[i]].push_back(i);
}
for(int i=1;i<=n;i++){
w[i]=read();
s[fa[i]]+=w[i];f[i]=i;
}
for(int i=1;i<=n;i++){
val[i]=s[i]-w[i];
pre[i]=(node){val[i],s[i],i,i};
q.insert(pre[i]);
}
vis[0]=1;
int now=0;
while(!q.empty()){
set<node>::iterator it=q.begin();
node x=*it;
q.erase(it);
if(vis[fa[x.h]]){//注意一个set(或者堆)中的节点维护的不是自己的值,而是一段路径的值,h,t分别是路径的最浅深度的位置和最深深度的位置
nxt[now]=x.h;
while(now!=x.t)vis[now]=1,now=nxt[now];
vis[now]=1;
}
else{
int pr=find(fa[x.h]);
q.erase(pre[pr]);
nxt[pre[pr].t]=pre[x.h].h;
pre[pr]=pre[pr]+pre[x.h];
f[find(x.h)]=pr;
q.insert(pre[pr]);
}
}
now=0;
for(int i=1;i<=n;i++){
now=nxt[now];
id[now]=i;
}
dfs(1,0);
for(int i=1;i<=n;i++)
printf("%lld ",ans[i]);
return 0;
}