ICode9

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

Codeforces600E Lomsat gelral

2022-03-28 21:34:12  阅读:226  来源: 互联网

标签:val res lol 儿子 fa gelral Codeforces600E 节点 Lomsat


Codeforces600E Lomsat gelral

题目链接

点我跳转

题目大意

一棵树,1号节点为根,每个结点都有一个颜色,第i个节点的颜色为ci。
如果一种颜色在以x为根的子树内出现次数最多(可以不唯一),称其在以x为根的子树中占主导地位。
你的任务是对于每一个i∈[1,n],求出以i为根的子树中,占主导地位的颜色的编号和。  
N<=10^5,ci<=n

Solution

dsu on tree (树上启发式合并)

不了解的可以看看这位大佬的博客,讲的很清楚

这里简述一下dsu的步骤就是:

\(dfs求出每个节点的重儿子\Rightarrow遍历当前节点的轻儿子\Rightarrow单独遍历当前节点的重儿子\Rightarrow计算重儿子的信息并返回给当前节点\Rightarrow计算轻儿子的信息\Rightarrow清除轻儿子的信息\)

(写在最前面)
记得开\(long\) \(long!!!\)
记得开\(long\) \(long!!!\)
记得开\(long\) \(long!!!\)

(进入正题)
开两个变量,\(res和maxn\)

lol res,maxn;//res保存当前数量最多的颜色的和,maxn保存当前数量最多的颜色的编号

然后我们通过以1为根节点遍历一遍树得到所有节点的重儿子

void dfs(lol u,lol fa) {
	sz[u]=1;
	for (lol i=head[u];i;i=nex[i]) {
		if(to[i]==fa) continue;
		dfs(to[i],u);
		sz[u]+=sz[to[i]];
		if(sz[to[i]]>sz[hson[u]]) hson[u]=to[i];//计算出每个节点的重儿子
	}
}

然后进行遍历,先只遍历当前节点的轻儿子,碰到父亲节点或者重儿子就跳过
单独遍历重儿子

val表示遍历的是重儿子还是轻儿子,重儿子(val=1)的信息要保留,轻儿子(val=0)的信息在统计上答案之后要清除,

void dsu(lol u,lol fa,lol val) {
	for (lol i=head[u];i;i=nex[i]) {
		if(to[i]==fa || to[i]==hson[u]) continue;
		dsu(to[i],u,0);
	}
	if(hson[u]) dsu(hson[u],u,1),HH=hson[u];
	calc(u,fa,1);
	HH=0;
	dp[u]=res;
	if(!val) calc(u,fa,-1),res=0,maxn=0;
}

在遍历完所有节点之后我们要再单独统计一下轻儿子的答案

这里说一下为什么单独再计算一次轻儿子的信息,因为重儿子的信息我们是保存下来的,但是轻儿子的信息每次计算后都会清除(这是dsu on tree的思想,重儿子只访问一遍,轻儿子要访问两遍),所以在统计以当前节点为根节点的答案的时候需要再统计一次轻儿子的信息

也就是这一段

calc(u,fa,1);
HH=0;
dp[u]=res;

到这里当前节点的答案就全部统计完毕了,我们可以保存下来,并把HH回溯(因为HH是全局变量,在返回上一层之后依然需要使用)

如果当前节点是作为轻儿子遍历的,那么我们在返回上一层时,需要清除当前节点的信息,即:

if(!val) calc(u,fa,-1),res=0,maxn=0;

下面是calc函数,用来计算轻儿子的信息,因此我们碰到重儿子的时候依然是需要跳过的
而当该函数用作清除函数的时候,此时HH必然已经清零,我们不会跳过以当前节点为根节点的子树中任何一个节点的信息

void calc(lol u,lol fa,lol val) {//val表示当前节点是统计信息还是清除信息
	if(val==1) {
		cnt[c[u]]++;
		if(cnt[c[u]]>maxn) maxn=cnt[c[u]],res=c[u];
		else if(cnt[c[u]]==maxn) res+=c[u];
	}
	else cnt[c[u]]--;
	for (lol i=head[u];i;i=nex[i]) {
		if(to[i]==fa || to[i]==HH) continue;
		calc(to[i],u,val);
	}
}

Code

#include<bits/stdc++.h>
#define lol long long//记得开long long
using namespace std;
const lol N=1e5+10;

lol n,cur;
lol head[N],nex[N<<1],to[N<<1];
lol cnt[N],dp[N];
lol sz[N];
lol hson[N];
lol HH;
lol c[N];
lol res,maxn;//res保存当前数量最多的颜色的和,maxn保存当前数量最多的颜色的编号

void add(lol a,lol b) {
	to[++cur]=b,nex[cur]=head[a],head[a]=cur;//建边
}

void dfs(lol u,lol fa) {
	sz[u]=1;
	for (lol i=head[u];i;i=nex[i]) {
		if(to[i]==fa) continue;
		dfs(to[i],u);
		sz[u]+=sz[to[i]];
		if(sz[to[i]]>sz[hson[u]]) hson[u]=to[i];//计算出每个节点的重儿子
	}
}

void calc(lol u,lol fa,lol val) {//val表示当前节点是统计信息还是清除信息
	if(val==1) {
		cnt[c[u]]++;
		if(cnt[c[u]]>maxn) maxn=cnt[c[u]],res=c[u];
		else if(cnt[c[u]]==maxn) res+=c[u];
	}
	else cnt[c[u]]--;
	for (lol i=head[u];i;i=nex[i]) {
		if(to[i]==fa || to[i]==HH) continue;
		calc(to[i],u,val);
	}
}

void dsu(lol u,lol fa,lol val) {
	for (lol i=head[u];i;i=nex[i]) {
		if(to[i]==fa || to[i]==hson[u]) continue;
		dsu(to[i],u,0);
	}
	if(hson[u]) dsu(hson[u],u,1),HH=hson[u];
	calc(u,fa,1);
	HH=0;
	dp[u]=res;
	if(!val) calc(u,fa,-1),res=0,maxn=0;
}

int main() {
	cin>>n;
	for (lol i=1;i<=n;i++) cin>>c[i];
	for (lol i=1,a,b;i<n;i++) {
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	dfs(1,0);
	dsu(1,0,0);
	for (lol i=1;i<=n;i++)
		cout<<dp[i]<<" \n"[i==n];
	return 0;
}

标签:val,res,lol,儿子,fa,gelral,Codeforces600E,节点,Lomsat
来源: https://www.cnblogs.com/real-l/p/16069197.html

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

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

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

ICode9版权所有