uoj418

题意

uoj

做法

下面考虑算\(ans_1\),也就是全局

将操作拍成一个序列,一个显然的贪心是放\(w_i\)后取出\(\sum\limits_{v\in son_i}w_v\),相当于到一个点\(i\)时,\(A_i=+w_i-\sum\limits_{v\in son_i}w_v\),求最大前缀和

这样会发生一个问题,就是怎么构造这个序列,先选完儿子才能选父亲,很不好搞

这样转换:把操作序列倒过来看,就是先选父亲,才能选儿子节点。
之前比如在相对序列中儿子值的变化:\(\{son_1(+w_{1}),...,son_2(+w_{2}),...,son_3(+w_3),...fa(-w_1-w_2-w_3)\}\)
现在等价于:\(\{fa(+w_1+w_2+w_3)\},...,son_3(-w_3),...,son_2(-w_2),...,son_1(-w_1)\)
对于序列上的某个点\(i\),定义二元组\((+\sum\limits_{v\in son_i}w_v-w_i,+\sum\limits_{v\in son_i}w_v)\)表示经过该点后的增量、历史最大增量

合并是显然的:\((x,y)+(x',y')=(x+x',max(y,x+y'))\)

然后这样的二元组是有严格的优先级的:

  • \(x<0\)的,优先放前面;多个\(x<0\)的,\(y\)较小的放前面
  • \((x,y),(x',y')(x,x'>0)\)\((x,y)\)放前面当且仅当:\(max(y,x+y')<max(y',x'+y)\),由于\(x,x'>0\),等价于\(x+y'<x'+y\Longrightarrow x-y<x'-y'\)

做全局解后,显然某个子树的解序列是全局解的子序列,如果我们能得到全局解序列,那么通过线段树合并容易计算每个点的答案

然后贪心的做这个:
当取出的点\(i\)其父亲还没取出来时,令其与父亲合并
我们需要得到全局解,要用链表维护

Code

上面讲的可能有点不清楚,放份代码帮助理解一下

#include<bits/stdc++.h>
typedef long long LL;
#define pb push_back
#define opt operator
#define pii std::pair<LL,LL>
const LL maxn=2e5+9;
LL Read(){
	LL x(0),f(1); char c=getchar();
	while(c<'0' || c>'9'){
		if(c=='-') f=-1; c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
	}return x*f;
}
namespace Uf{
	LL fa[maxn];
	void Init(LL N){
		for(LL i=1;i<=N;++i) fa[i]=i;
	}
	LL Find(LL x){
		return fa[x]==x?x:fa[x]=Find(fa[x]);
	}
}
struct node{
	LL x,y,id;
	bool opt < (const node A)const{
		if(x<0 && A.x>=0) return 1;
		if(A.x<0 && x>=0) return 0;
		if(x<0 && A.x<0){
			return (y<A.y || (y==A.y && id<A.id));
		}
		LL xx(y-x),yy(A.y-A.x);
		return (xx>yy || (xx==yy && id<A.id));
	}
	node opt + (const node A)const{
		return (node){x+A.x,std::max(y,x+A.y),id};
	}
};
namespace Sgt{
	LL nod;
	LL rt[maxn],son[maxn*20][2];
	node val[maxn*20];
    void Modify(LL &nw,LL l,LL r,LL x,node v){
		nw=++nod; val[nw]=v;
		if(l==r){
			return;
		}
		LL mid(l+r>>1);
		if(x<=mid) Modify(son[nw][0],l,mid,x,v);
		else Modify(son[nw][1],mid+1,r,x,v);
	}
	LL Merge(LL x,LL y){
		if(!x || !y) return x|y;
		son[x][0]=Merge(son[x][0],son[y][0]);
		son[x][1]=Merge(son[x][1],son[y][1]);
		val[x]=val[son[x][0]]+val[son[x][1]];
		return x;
	}
}
LL n,tot,T;
LL ans[maxn],val[maxn],w[maxn],nxt[maxn],head[maxn],tail[maxn],vis[maxn];
LL pos[maxn],fa[maxn];
node a[maxn];
std::vector<LL> V[maxn];
void Dfs(LL u){
	Sgt::Modify(Sgt::rt[u],1,n,pos[u],(node){val[u]-w[u],val[u]});
	for(LL i=0;i<V[u].size();++i){
		LL v(V[u][i]);
		Dfs(v);
		Sgt::rt[u]=Sgt::Merge(Sgt::rt[u],Sgt::rt[v]);
	}
	ans[u]=Sgt::val[Sgt::rt[u]].y+w[u];
}
std::set<node> Set;
std::set<node>::iterator it1;
void Init(){
	for(LL i=1;i<=n;++i){
		val[fa[i]]+=w[i];
	}
	for(LL i=1;i<=n;++i){
		a[i]=(node){val[i]-w[i],val[i],i};
		Set.insert(a[i]);
		head[i]=i; tail[i]=i;
	}
	Uf::Init(n);
}
void Update(LL u){
	LL x(head[u]);
	while(x){
		++tot; pos[x]=tot;
		vis[x]=1;
		x=nxt[x];
	}
}
void Merge(LL f,LL u){
	Uf::fa[u]=f;
	nxt[tail[f]]=head[u];
	tail[f]=tail[u];
	it1=Set.find(a[f]); Set.erase(it1);
	a[f]=a[f]+a[u];
	Set.insert(a[f]);
}
void Solve(){
	memset(vis,0,sizeof(vis));
	vis[0]=1;
	while(!Set.empty()){
		it1=Set.begin(); Set.erase(it1);
		LL u((*it1).id),f(Uf::Find(fa[u]));
		if(vis[f]){
			Update(u);
		}else{
			Merge(f,u);
		}
	}
}
int main(){
	T=Read();
	n=Read();
	for(LL i=2;i<=n;++i){
		LL x(Read());
		fa[i]=x; V[x].pb(i);
	}
    for(LL i=1;i<=n;++i){
		w[i]=Read();
	}
	Init();
	Solve();
	Dfs(1);
	for(LL i=1;i<=n;++i) printf("%lld ",ans[i]);
	puts("");
	return 0;
}
posted @ 2020-05-03 17:17  Grice  阅读(112)  评论(0编辑  收藏  举报