ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1578 J

2021-10-07 14:03:52  阅读:175  来源: 互联网

标签:tmp CF1578 fu int siz vv 权值


题目

树上每个节点都有自己的需要权值,\(0\) 号点为根与源点,每次从 \(0\) 号点发 \(d\) 的权值,一个点接到权值之后会查看自己的儿子:如果有儿子还没有被满足,那么就会将权值平分给没有被满足的儿子。否则如果自己没被满足,就给自己留下权值,如果自己已经满足了,就把多余的权值返还给父亲。

询问分别使 \(i=1...n\) 中每个点被满足时从 \(0\) 号点发送的最小权值

数据范围

\(n \leq 3\times 10^5\)

题解

正着考虑很奇怪,因为同时有许许多多分数的权值在上下游走,那么考虑倒着推回去。

设当前需要满足的点为 \(u\) ,需求为 \(a_u\) ,那么可以发现与 \(u\) 同属于一个父亲的每个点 \(v\) ,在 \(u\) 被满足的时候都获得了 \(min(a_v,a_u)\) 的权值,那么我们就可以倒推出点 \(u\) 父亲获得了多少权值,递归下去推出从 \(0\) 出发了多少权值。

现在可以计算答案了,那么考虑如何快速的从 \(u\) 跳到 \(0\) ,设点 \(x\) 当前答案为 \(ans\) ,那么计算同一层的答案就是 \(\sum \min(ans,siz_u)\) ,不难发现如果 \(ans \geq \forall siz_u\) ,那么就是加上 \(\sum siz_u\) ,否则发现 \(ans\) 一定翻倍,而 \(ans\) 最多翻 \(log\) 倍,那么只需要每次知道往上跳的时候什么时候 \(ans\) 翻倍,中间的用倍增快速跳过就好了。

考虑维护出来一个倍增数组 \(f_{u,i}\) 表示从 \(u\) 点往上跳 \(2^i\) 步且不翻倍,当前答案必须 \(\geq f_{u,i}\) ,那么再维护出来 \(s_{u,i}\) 表示从点 \(u\) 往上跳 \(2^i\) 步且不翻倍答案增加多少,则转移为 \(f_{u,i}=\max (f_{u,i-1},f_{g_{u,i-1},i-1}-s_{u,i-1})\)

PS:这道题写的时候不小心把一个地方实现错了。。写多次在树上遍历一个点所有儿子还是要谨慎。。不然还真不好发现这种问题

const int N=6e5+5;
int n,fu[N],a[N],deep[N],g[N][19];
ll f[N][19],maxx[N],mixx[N];
ll s[N][19];
ll siz[N];
vi v[N],vv[N],sa[N];
double st,ed;
void dfs(int u){
	siz[u]=a[u];deep[u]=deep[fu[u]]+1;g[u][0]=fu[u];
	vv[u].pb(0);
	for(auto i:v[u]){
		dfs(i);
		siz[u]+=siz[i];
		if(siz[i]>maxx[u])mixx[u]=maxx[u],maxx[u]=siz[i];
		else if(siz[i]>mixx[u])mixx[u]=siz[i];
		vv[u].pb(siz[i]);
	}
	sort(vv[u].begin(),vv[u].end());
	sa[u].resize(vv[u].size()+1);
	sa[u][0]=vv[u][0];
	for(int i=1;i<(int)vv[u].size();i++)sa[u][i]=sa[u][i-1]+vv[u][i];
}
void solve(int u){
	ll tmp=siz[u];
	while(u){
		for(int i=18;i>=0;i--){
			if(tmp>=f[u][i]){
				tmp=tmp+s[u][i];
				u=g[u][i];
			}
		}
		if(u){
			ll pre=tmp;
			int tt=lower_bound(vv[fu[u]].begin(),vv[fu[u]].end(),pre)-vv[fu[u]].begin()-1;
			tmp=tmp+sa[fu[u]][tt]+(vv[fu[u]].size()-tt-1)*pre-pre;
			u=fu[u];
		}
	}
	print(tmp);
}
signed main(){
	#ifdef newbiewzs
	#else
	#endif
	st=clock();
	n=read();
	for(int i=1;i<=n;i++){
		fu[i]=read();a[i]=read();
		v[fu[i]].pb(i);
	}
	dfs(0);
	for(int i=1;i<=n;i++){
		f[i][0]=s[i][0]=siz[fu[i]]-a[fu[i]]-siz[i];
	}
	for(int i=1;i<=18;i++){
		for(int k=1;k<=n;k++){
			g[k][i]=g[g[k][i-1]][i-1];
			s[k][i]=(s[k][i-1]+s[g[k][i-1]][i-1]);
			f[k][i]=max(f[k][i-1],f[g[k][i-1]][i-1]-s[k][i-1]);
		}
	}
	
	for(int i=1;i<=n;i++){
		solve(i);
	}
	Flush();
	return 0;
}

标签:tmp,CF1578,fu,int,siz,vv,权值
来源: https://www.cnblogs.com/newbiewzs/p/15375749.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有