其他分享
首页 > 其他分享> > Codeforces600E Lomsat gelral

Codeforces600E Lomsat gelral

作者:互联网

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