uoj 418

原题

题意:

Snuke 有一棵 n n 个点的有根树,每个点有权值 w i wi ,初始每个结点上都没有石子。

Snuke 准备了一些石子,并把它们拿在手中。她可以进行以下两种操作任意多次:

1 从手中取 w i wi 个石子放在结点 i i 上,进行该操作要求结点 i i 的所有孩子 j j 上都有 w j wj 个石子。

2 将结点 i i 上的所有石子收回手中。

Takahashi 想知道对于每个 i i ,为了在结点 i i 上放 w i wi 个石子,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;
}

发布了62 篇原创文章 · 获赞 1 · 访问量 1014

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103702360