1用于离线处理不带修改的子树问题,维护子树信息
2启发式合并,将小的集合往大的集合合并,判断树上的大小集合,可以分为轻重节点,像重链剖分一样
3有两次dfs,第一次dfs获取重节点
4第二次dfs获取答案,需要遵循以下操作
(1)优先获取轻子节点及其子树答案,然后删去轻子节点对当前节点u的答案贡献
(2)获取重子节点及其子树的答案,但不删取重子节点对当前答案的贡献
(3)遍历所有轻子节点及其子树,结合u本身和重子节点,获取u的答案
5(1)和(3)不能一起做,因为会导致贡献重复计算。
6(个人理解)u节点需要结合它的子节点及其子树获取信息,需要遍历完所有子树才能获得u的答案,各子节点及其子树获取答案时互不干扰,但因为空间有限,需要共用一块计算答案的数据,因此遍历完一棵子树时,需要删掉其贡献,但是最后遍历的子树对其他子树的遍历不会有影响,所以可以保留一块子树的贡献,那么肯定保留尽量大的,获取该子树答案的同时,顺便也在获取u节点的答案
代码实现
根据模板题:Problem - 600E - Codeforces
题意:一棵n个节点的树,每个节点有各自的颜色,以1为根节点,求每个节点的子树中相同颜色最大的数量,(如果子树中两种颜色数量都是最大,两个颜色都算上,答案就是它们的和)
1数据存储
const int maxn=1e5+10;
vectorvv[maxn];
inline void add(int u,int v)
{
vv[u].push_back(v);
vv[v].push_back(u);
}
int siz[maxn],son[maxn];//记录重子节点
int cnt[maxn];//记录当前节点子树的i的颜色的数量,这个当前节点会不断变动
int col[maxn];//记录节点的颜色
int flag;//标记重重子节点
ll ans[maxn],res,maxc;//记录每个节点的答案,res表示当前的答案,maxc表示最多的颜色的数量
2第一次dfs(获取重子节点)
void dfs1(int u,int fa)//寻找重子节点
{
siz[u]=1;
for(int to:vv[u])
{
if(fa==to)
continue;
dfs1(to,u);
siz[u]+=siz[to];
if(siz[to]>siz[son[u]])
son[u]=to;
}
}
3计算贡献,获取当前答案
void count(int u,int fa,int val)//计算贡献,val为正时添加贡献,val为负时删除贡献
{
cnt[col[u]]+=val;
if(cnt[col[u]]>maxc)//如果当前节点的元素数量大于maxc
{
maxc=cnt[col[u]];
res=col[u];
}
else if(cnt[col[u]]==maxc)//如果当前元素数量等于maxc,那么就是并列最大
res+=col[u];
for(int to:vv[u])//计算子树贡献
{
if(to==fa||to==flag)
continue;
count(to,u,val);
}
}
4第二次dfs
void dfs2(int u,int fa,int keep)//keep代表是否要删除贡献 0为删除,1为保留
{
for(int to:vv[u])//先遍历轻子节点 ,同时计算轻子节点及其子树答案
{
if(to==fa||to==son[u])
continue;
dfs2(to,u,0);
}
if(son[u])//遍历重子节点,同时计算重子节点及其子树答案
{
dfs2(son[u],u,1);
flag=son[u];//标记重子节点,保证u在遍历轻子节点时不往该重子节点跑
}
count(u,fa,1);//计算贡献
flag=0;
ans[u]=res;//计算答案
if(!keep)
{
count(u,fa,-1);//删除贡献
res=maxc=0;
}
}
5主函数
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>col[i];
for(int i=1;i>u>>v;
add(u,v);
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++)
if(i==1)
cout<
6完整代码
#include
#define ll long long
using namespace std;
const int maxn=1e5+10;
vectorvv[maxn];
inline void add(int u,int v)
{
vv[u].push_back(v);
vv[v].push_back(u);
}
int siz[maxn],son[maxn];//记录重子节点
int cnt[maxn];//记录当前节点子树的i的颜色的数量,这个当前节点会不断变动
int col[maxn];//记录节点的颜色
int flag;//标记重重子节点
ll ans[maxn],res,maxc;//记录每个节点的答案,res表示当前的答案,maxc表示最多的颜色的数量
void dfs1(int u,int fa)//寻找重子节点
{
siz[u]=1;
for(int to:vv[u])
{
if(fa==to)
continue;
dfs1(to,u);
siz[u]+=siz[to];
if(siz[to]>siz[son[u]])
son[u]=to;
}
}
void count(int u,int fa,int val)//计算贡献,val为正时添加贡献,val为负时删除贡献
{
cnt[col[u]]+=val;
if(cnt[col[u]]>maxc)//如果当前节点的元素数量大于maxc
{
maxc=cnt[col[u]];
res=col[u];
}
else if(cnt[col[u]]==maxc)//如果当前元素数量等于maxc,那么就是并列最大
res+=col[u];
for(int to:vv[u])//计算子树贡献
{
if(to==fa||to==flag)
continue;
count(to,u,val);
}
}
void dfs2(int u,int fa,int keep)//keep代表是否要删除贡献 0为删除,1为保留
{
for(int to:vv[u])//先遍历轻子节点 ,同时计算轻子节点及其子树答案
{
if(to==fa||to==son[u])
continue;
dfs2(to,u,0);
}
if(son[u])//遍历重子节点,同时计算重子节点及其子树答案
{
dfs2(son[u],u,1);
flag=son[u];//标记重子节点,保证u在遍历轻子节点时不往该重子节点跑
}
count(u,fa,1);//计算贡献
flag=0;
ans[u]=res;//计算答案
if(!keep)
{
count(u,fa,-1);//删除贡献
res=maxc=0;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>col[i];
for(int i=1;i>u>>v;
add(u,v);
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++)
if(i==1)
cout<