【树上问题杂谈】
简介
好,我摸爬滚打了好几天,干掉了数论
接下来找树了(
可能主要是一些树上维护信息,树形\(DP\)放到以后
主体
树上问题的一些基本方法
- 求\(LCA\)
- \(DFS\)序
- 链差分
- 树形\(DP\)
\(LCA\)
- 树链剖分(好打且复杂度较优)
- \(RMQ\)(这个我不会)
- 倍增
点权和:\(d_u + d_v - d_l - d_{p_l}\)
边权和: \(d_u + d_v - 2 * d_l\)
\(DFS\)序
在\(DFS\)时计入一个点进入搜索树的时间点叫做\(DFS\)序,一颗子树对应的是\(DFS\)上的一个区间,如果也记录离开搜索树的时间戳,叫做欧拉序或括号序
括号序和\(DFS\)序,可以用来\(O(1)\)的判断一个点是否是另外一个点的子树。
树链剖分
把线段树可做的东西搬到树上,区间操作变成链操作和子树操作
同时树链剖分支持换根
#include<iostream>
#include<cstdio>
#define ll long long
struct P{
ll to,next;
}e[400000];
ll n,m,root,p;
ll v[200000],head[200000];
ll cnt = 0;
void add(ll x,ll y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
int dep[200000],fa[200000],siz[200000],gson[200000];
int dfncnt,dfn[200000],top[200000],num[200000];
void dfs2(int u,int t){
top[u] = t;
dfn[u] = ++dfncnt;
num[dfncnt] = v[u];
if(!gson[u])return;
dfs2(gson[u],t);
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa[u] || v == gson[u])
continue;
dfs2(v,v);
}
}
void dfs(ll u,ll f){
fa[u] = f;
dep[u] = dep[f] + 1;
siz[u] = 1;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == f)
continue;
dfs(v,u);
siz[u] += siz[v];
if(siz[v] > siz[gson[u]])
gson[u] = v;
}
}
struct Seg{
struct E{
ll l,r,v,m;
}t[400000];
#define l(x) t[x].l
#define r(x) t[x].r
#define v(x) t[x].v
#define m(x) t[x].m
#define mid ((l + r) >> 1)
void up(ll now){v(now) = (v(l(now)) + v(r(now))) % p;}
void push(ll now,ll l,ll r){
m(l(now)) += m(now);
m(r(now)) += m(now);
v(l(now)) += m(now) * (mid - l + 1);
v(r(now)) += m(now) * (r - mid);
v(l(now)) %= p,m(l(now)) %= p;
v(r(now)) %= p,m(r(now)) %= p;
m(now) = 0;}
ll build(ll l,ll r){
ll now = ++cnt;
m(now) = 0;
if(l == r){
v(now) = num[l];
return cnt;
}
l(now) = build(l,mid);
r(now) = build(mid + 1,r);
up(now);
return now;
}
void change(ll now,ll l,ll r,ll nl,ll nr,ll s){
push(now,l,r);
if(nl <= l && r <= nr){
v(now) += (r - l + 1) * s;
m(now) += s;
v(now) %= p;
m(now) %= p;
return ;
}
if(nl <= mid)
change(l(now),l,mid,nl,nr,s);
if(nr > mid)
change(r(now),mid + 1,r,nl,nr,s);
up(now);
}
ll q(ll now,ll l,ll r,ll nl,ll nr){
push(now,l,r);
ll ans = 0;
if(nl <= l && r <= nr)
return v(now) % p;
if(nl <= mid)
ans = (ans + q(l(now),l,mid,nl,nr)) % p;
if(nr > mid)
ans = (ans + q(r(now),mid + 1,r,nl,nr)) % p;
up(now);
return ans;
}
}Q;
int main(){
scanf("%lld%lld%lld%lld",&n,&m,&root,&p);
for(int i = 1;i <= n;++i){
scanf("%lld",&v[i]);
}
for(int i = 1;i <= n - 1;++i){
ll x,y;
scanf("%lld%lld",&x,&y);
add(x,y);
add(y,x);
}
dfs(root,0);
dfs2(root,root);
cnt = 0;
int z = Q.build(1,n);
// for(int i = 1;i <= n;++i){
// std::cout<<fa[i]<<" "<<dep[i]<<" "<<top[i]<<" "<<dfn[i]<<" "<<siz[i]<<" "<<gson[i]<<std::endl;
// }
for(int i = 1;i <= m;++i){
ll opt,x,y,z;
scanf("%lld",&opt);
if(opt == 1){
scanf("%lld%lld%lld",&x,&y,&z);
z %= p;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])std::swap(x,y);
Q.change(1,1,n,dfn[top[x]],dfn[x],z);
x = fa[top[x]];
}
if(dep[x] > dep[y]) std::swap(x,y);
Q.change(1,1,n,dfn[x],dfn[y],z);
}
if(opt == 2){
scanf("%lld%lld",&x,&y);
ll ans = 0;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])std::swap(x,y);
ans = (ans + Q.q(1,1,n,dfn[top[x]],dfn[x])) % p;
x = fa[top[x]];
}
if(dep[x] > dep[y])
std::swap(x,y);
ans = (ans + Q.q(1,1,n,dfn[x],dfn[y])) % p;
std::cout<<ans % p<<std::endl;
}
if(opt == 3){
scanf("%lld%lld",&x,&z);
Q.change(1,1,n,dfn[x],dfn[x] + siz[x] - 1,z);
}
if(opt == 4){
scanf("%lld",&x);
std::cout<<Q.q(1,1,n,dfn[x],dfn[x] + siz[x] - 1) % p<<std::endl;
}
}
}//一个普通的维护路径权和的代码,还是写的不够快
#include<iostream>
#include<cstdio>
#define ll long long
struct P{
ll to,next;
}e[500005 << 1];
ll n,m,root,p;
ll head[500005];
ll cnt = 0;
void add(ll x,ll y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
int dep[500005],fa[500005],siz[500005],gson[500005];
int dfncnt,dfn[500005],top[500005];
void dfs2(int u,int t){
top[u] = t;
dfn[u] = ++dfncnt;
if(!gson[u])return;
dfs2(gson[u],t);
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa[u] || v == gson[u])
continue;
dfs2(v,v);
}
}
void dfs(ll u,ll f){
fa[u] = f;
dep[u] = dep[f] + 1;
siz[u] = 1;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == f)
continue;
dfs(v,u);
siz[u] += siz[v];
if(siz[v] > siz[gson[u]])
gson[u] = v;
}
}
int main(){
scanf("%lld%lld%lld",&n,&m,&root);
for(int i = 1;i <= n - 1;++i){
ll x,y;
scanf("%lld%lld",&x,&y);
add(x,y);
add(y,x);
}
dfs(root,0);
dfs2(root,root);
for(int i = 1;i <= m;++i){
ll x,y;
scanf("%lld%lld",&x,&y);
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]]) std::swap(x,y);
x= fa[top[x]];
}
if(dep[x] > dep[y])std::swap(x,y);
std::cout<<x<<std::endl;
}
}//树剖求LCA,我可能写丑了,并不很快
同时,需要知道的是,树剖是可以支持换根操作的,考虑换根之后对各种操作的影响即可
CF916E
#include <cstdio>
#include <algorithm>
#define lson rt<<1
#define rson rt<<1|1
const int N=1e5+5,M=2e5+5,logN=17+1;
int n,m,root,idx,a[N],f[N][logN],dfn[N],seq[N],sz[N],dep[N];
int tot,lnk[N],ter[M],nxt[M];
long long seg[N<<2],tag[N<<2];
void pushup(int rt) {
seg[rt]=seg[lson]+seg[rson];
}
void build(int rt,int l,int r) {
if(l==r) {
seg[rt]=a[seq[l]];
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void update(int rt,int l,int r,long long k) {
seg[rt]+=1LL*(r-l+1)*k;
tag[rt]+=k;
}
void pushdown(int rt,int l,int r) {
if(!tag[rt]) return;
int mid=(l+r)>>1;
update(lson,l,mid,tag[rt]);
update(rson,mid+1,r,tag[rt]);
tag[rt]=0;
}
void modify(int x,int y,int rt,int l,int r,int k) {
if(x<=l&&r<=y) {
update(rt,l,r,k);
return;
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(x<=mid) modify(x,y,lson,l,mid,k);
if(mid<y) modify(x,y,rson,mid+1,r,k);
pushup(rt);
}
long long query(int x,int y,int rt,int l,int r) {
if(x<=l&&r<=y) return seg[rt];
pushdown(rt,l,r);
int mid=(l+r)>>1;
long long ret=0;
if(x<=mid) ret+=query(x,y,lson,l,mid);
if(mid<y) ret+=query(x,y,rson,mid+1,r);
return ret;
}
void add(int u,int v) {
ter[++tot]=v,nxt[tot]=lnk[u],lnk[u]=tot;
}
void dfs(int u,int fa) {
dep[u]=dep[fa]+1,f[u][0]=fa,dfn[u]=++idx,seq[idx]=u,sz[u]=1;
for(int i=1;(1<<i)<=dep[u];++i) f[u][i]=f[f[u][i-1]][i-1];
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i];
if(v==fa) continue;
dfs(v,u),sz[u]+=sz[v];
}
}
int lca(int u,int v) {
if(dep[u]>dep[v]) u^=v^=u^=v;
for(int i=17;~i;--i) if(dep[f[v][i]]>=dep[u]) v=f[v][i];
if(u==v) return u;
for(int i=17;~i;--i) if(f[u][i]^f[v][i]) u=f[u][i],v=f[v][i];
return f[u][0];
}
int getlca(int u,int v,int p) {
int x=lca(u,v),y=lca(u,p),z=lca(v,p);
if(dep[y]>dep[x]) x=y;
if(dep[z]>dep[x]) x=z;
return x;
}
int jump(int u,int d) {
for(int i=17;~i;--i) if(d&(1<<i)) u=f[u][i];
return u;
}
void treeModify(int u,int k) {
int l=dfn[u],r=dfn[u]+sz[u]-1;
if(u==root) modify(1,n,1,1,n,k);
else if(dfn[root]<l||dfn[root]>r) modify(l,r,1,1,n,k);
else {
int son=jump(root,dep[root]-dep[u]-1);
modify(1,n,1,1,n,k),modify(dfn[son],dfn[son]+sz[son]-1,1,1,n,-k);
}
}
long long treeQuery(int u) {
int l=dfn[u],r=dfn[u]+sz[u]-1;
if(u==root) return query(1,n,1,1,n);
else if(dfn[root]<l||dfn[root]>r) return query(l,r,1,1,n);
else {
int son=jump(root,dep[root]-dep[u]-1);
return query(1,n,1,1,n)-query(dfn[son],dfn[son]+sz[son]-1,1,1,n);
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<n;++i) {
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
build(1,1,n);
root=1;
while(m--) {
int opt;
scanf("%d",&opt);
if(opt==1) {
scanf("%d",&root);
} else if(opt==2) {
int u,v,x;
scanf("%d%d%d",&u,&v,&x);
treeModify(getlca(u,v,root),x);
} else {
int x;
scanf("%d",&x);
printf("%lld\n",treeQuery(x));
}
}
return 0;
}
虚树
原本有很麻烦的做法的,但因为\(pbpb\)鸽鸽的一个做法,一切变得简单起来。
寻宝游戏
[SDOI2015]寻宝游戏
假设关键点按照 \(DFS\) 序排序后是\({a1,a2,…,ak}\)。
那么所有关键点形成的极小联通子树的边权和的两倍等于 \(dist(a1,a2)+dist(a2,a3)+⋯+dist(ak−1,ak)+dist(ak,a1)\)。
然后\(set\)维护一手,\(lca\)什么的树剖就好了
#include<iostream>
#include<cstdio>
#include<set>
#define ll long long
ll n,q;
struct P{
ll to,next,v;
}e[200005];
ll cnt,head[200005];
void add(ll x,ll y,ll v){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].v = v;
head[x] = cnt;
}
ll dcnt,fa[200005],idf[200005],dep[200005],siz[200005],son[200005],top[200005],dfn[200005],s[200005];
void dfs1(int now,int f){
fa[now] = f;
dep[now] = dep[f] + 1;
siz[now] = 1;
for(int i = head[now];i;i = e[i].next){
int v = e[i].to;
if(v == f)
continue;
s[v] = s[now] + e[i].v;
dfs1(v,now);
siz[now] += siz[v];
if(siz[v] > siz[son[now]])
son[now] = v;
}
}
void dfs2(int now,int t){
dfn[now] = dcnt ++ ;
top[now] = t;
idf[dfn[now]] = now;
if(!son[now])
return;
dfs2(son[now],t);
for(int i = head[now];i;i = e[i].next){
int v = e[i].to;
if(v == fa[now] || v == son[now])
continue;
dfs2(v,v);
}
}
std::set<int>st;
std::set<int>::iterator it;
ll ans = 0;
ll lca(ll x,ll y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])std::swap(x,y);
x = fa[top[x]];
}
if(dep[x] > dep[y])std::swap(x,y);
return x;
}
ll dist(ll x,ll y){return s[x] + s[y] - 2 * s[lca(x,y)];}
bool vis[200005];
int main(){
scanf("%lld%lld",&n,&q);
for(int i = 1;i < n;++i){
ll x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
dfs1(1,0);
dfs2(1,1);
for(int i = 1;i <= q;++i){
ll x;
scanf("%lld",&x);
x = dfn[x];
if(!vis[idf[x]])st.insert(x);
ll y = idf[(it = st.lower_bound(x)) == st.begin() ? * -- st.end() : * --it];
ll z = idf[(it = st.upper_bound(x)) == st.end() ? *st.begin() : * it];
if(vis[idf[x]])st.erase(x);
x = idf[x];
ll d = dist(x,y) + dist(x,z) - dist(y,z);
if(!vis[x])vis[x] = 1,ans += d;
else
vis[x] = 0,ans -= d;
std::cout<<ans<<std::endl;
}
return 0;
}
[SDOI2011]消耗战
[SDOI2011]消耗战
板子题,代码明天给补上。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define M 1000005
#define N 500005
struct P{
ll to,next,v;
}e[M],e2[M];
ll cnt,head[N],dfn[N],idf[N],top[N],Minn[N],dep[N],fa[N],siz[N],son[N];
ll dcnt;
ll cnt2,head2[N],DP[N];
void add(ll x,ll y,ll v){
e[++cnt].to = y;
e[cnt].v = v;
e[cnt].next = head[x];
head[x] = cnt;
}
void add2(ll x,ll y){
e2[++cnt2].to = y;
e2[cnt2].next = head2[x];
head2[x] = cnt2;
}
void dfs1(ll now,ll f){
fa[now] = f;
dep[now] = dep[f] + 1;
siz[now] = 1;
for(int i = head[now];i;i = e[i].next){
ll v = e[i].to;
if(v != f){
Minn[v] = std::min(Minn[now],e[i].v);
dfs1(v,now);
siz[now] += siz[v];
if(siz[v] > siz[son[now]])
son[now] = v;
}
}
}
void dfs2(ll now,ll t){
top[now] = t;
dfn[now] = ++dcnt;
idf[dfn[now]] = now;
if(!son[now])
return;
dfs2(son[now],t);
for(int i = head[now];i;i = e[i].next){
ll v = e[i].to;
if(v == fa[now] || v == son[now])
continue;
dfs2(v,v);
}
}
ll lca(ll x,ll y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])
std::swap(x,y);
x = fa[top[x]];
}
if(dep[x] > dep[y])
std::swap(x,y);
return x;
}
ll stack[N];
void build(){
cnt2 = 0;
std::sort(stack + 1,stack + stack[0] + 1);
ll s = stack[0];
for(int i = 2;i <= s;++i)
stack[++stack[0]] = dfn[lca(idf[stack[i]],idf[stack[i - 1]])];
std::sort(stack + 1,stack + stack[0] + 1);
s = std::unique(stack + 1,stack + stack[0] + 1) - stack - 1;
for(int i = 2;i <= s;++i)
add2(lca(idf[stack[i]],idf[stack[i - 1]]),idf[stack[i]]);
}
bool qr[N];
void dfs(ll now){
ll s = 0;
DP[now] = Minn[now];
for(int i = head2[now];i;i = e2[i].next){
ll v = e2[i].to;
dfs(v);
s += DP[v];
}
if(s && !qr[now])
DP[now] = std::min(s,DP[now]);
else
qr[now] = 0;
head2[now] = 0;
}
ll n,q;
int main(){
scanf("%lld",&n);
std::memset(Minn,0x7f7f7f7f,sizeof(Minn));
for(int i = 1;i <= n - 1;++i){
ll x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
dfs1(1,0);
dfs2(1,1);
scanf("%lld",&q);
while(q -- ){
stack[0] = 0;
ll x;
scanf("%lld",&x);
for(int i = 1;i <= x;++i){
ll s;
scanf("%lld",&s);
qr[s] = 1;
stack[++stack[0]] = dfn[s];
}
stack[++stack[0]] = dfn[1];
build();
dfs(1);
std::cout<<DP[1]<<std::endl;
}
}
淀粉质
正如\(fdy\)老师讲的一样,淀粉质实际上不过是建出重心重构树,然后分治罢了
Tree
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define N 40005
#define M 80005
struct P{
int to,next,v;
}e[M];
int cnt,head[N],dis[N],siz[N],dp[N],rev[N];
bool vis[N];
void add(int x,int y,int v){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].v = v;
head[x] = cnt;
}
ll n,k,rt,sum;
void getrt(ll u,ll fa){
siz[u] = 1,dp[u] = 0;
for(int i = head[u];i;i = e[i].next){
ll v = e[i].to;
if(v == fa || vis[v])
continue;
getrt(v,u);
siz[u] += siz[v];
dp[u] = std::max(dp[u],siz[v]);
}
dp[u] = std::max((ll)dp[u],sum - siz[u]);
if(dp[u] < dp[rt])
rt = u;
}
ll ans = 0;
ll tot = 0;
void getdis(ll u,ll fa){
rev[++tot] = dis[u];
for(int i = head[u];i;i = e[i].next){
ll v = e[i].to;
if(v == fa || vis[v])
continue;
dis[v] = dis[u] + e[i].v;
getdis(v,u);
}
}
ll doit(ll u,ll s){
tot = 0,dis[u] = s,getdis(u,0);
std::sort(rev + 1,rev + tot + 1);
ll l = 1,r = tot;
ll ans = 0;
while(l <= r)
if(rev[l] + rev[r] <= k)
ans += r - l,++l;
else
--r;
return ans;
}
void solve(ll u){
vis[u] = 1,ans += doit(u,0);
for(int i = head[u];i;i = e[i].next){
ll v = e[i].to;
if(vis[v])continue;
ans -= doit(v,e[i].v);
sum = siz[v],dp[0] = n,rt = 0;
getrt(v,u),solve(rt);
}
}
int main(){
scanf("%lld",&n);
for(int i = 1;i < n;++i){
ll x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
scanf("%lld",&k);
dp[0] = sum = n,getrt(1,0),solve(rt);
std::cout<<ans<<std::endl;
}
[国家集训队]聪聪可可
[国家集训队]聪聪可可
!!!记得求重心的时候\(dp[u]\)要清$0!!
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 20005
#define M 40005
struct P{
int to,v,next;
}e[M];
int cnt,head[N];
void add(int x,int y,int v){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].v = v;
head[x] = cnt;
}
ll sum,rt,siz[N],dp[N];
bool vis[N];
void get_rt(int u,int fa){
siz[u] = 1,dp[u] = 0;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa || vis[v])
continue;
get_rt(v,u);
siz[u] += siz[v];
dp[u] = std::max(siz[v],dp[u]);
}
dp[u] = std::max(sum - siz[u],dp[u]);
if(dp[u] < dp[rt])
rt = u;
}
ll dcnt[4],dis[N];
ll tot = 0;
void get_dis(int u,int fa){
dcnt[dis[u] % 3] ++ ;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa || vis[v])
continue;
dis[v] = dis[u] + e[i].v;
get_dis(v,u);
}
}
ll ans = 0;
ll doit(int u,int s){
dcnt[0] = dcnt[1] = dcnt[2] = 0;
dis[u] = s;
get_dis(u,0);
ll ans = 0;
ans += dcnt[0] * dcnt[0];
ans += dcnt[1] * dcnt[2] * 2;
return ans;
}
ll n;
void solve(int u){
vis[u] = 1,ans += doit(u,0);
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(vis[v])continue;
ans -= doit(v,e[i].v);
sum = siz[v],dp[0] = n,rt = 0;
get_rt(v,u),solve(rt);
}
}
ll gcd(ll a,ll b){return (!b) ? a : gcd(b,a % b);}
int main(){
scanf("%lld",&n);
for(int i = 1;i <= n - 1;++i){
ll x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
sum = n,dp[0] = n,rt = 0;
get_rt(1,0),solve(rt);
ll g = gcd(ans,n * n);
printf("%lld/%lld",(ans / g),(n * n / g));
}
树上启发式合并
考虑暴力进行一些操作,这样我们发现,对于每颗子树,我们做完这颗子树后,由于不同子树之间不能互相影响,所以需要进行一个清空操作,我们可以通过调换顺序,来保证复杂度,考虑把重儿子的子树放在最后进行操作,这样这颗子树的答案不用清空可以和答案直接合并。
CF600E Lomsat gelral
CF600E Lomsat gelral
(注意遍历子树是\(e[i].next\)不是\(e[i].to\),写错很有可能通过输出无法查出错误,这种情况不如检查一下这里)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define N 100005
#define M 200005
struct P{
ll to,next;
}e[M];
int cnt,C[N],head[N],c[N],siz[N],gson[N];
ll ans[N],fans[N];
void add(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
ll n;
void dfs(int u,int fa){
siz[u] = 1;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa)
continue;
dfs(v,u);
siz[u] += siz[v];
if(siz[v] > siz[gson[u]])
gson[u] = v;
}
}
std::queue<int>QWQ;
ll maxx;
void clear(){
maxx = 0;
while(!QWQ.empty()){
C[QWQ.front()] = 0;
QWQ.pop();
}
}
ll t;
void init(int u,int fa){
QWQ.push(c[u]);
C[c[u]] ++ ;
if(C[c[u]] > maxx)
fans[t] = c[u],maxx = C[c[u]];
else
if(C[c[u]] == maxx)
fans[t] += c[u];
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa || v == gson[t])
continue;
init(v,u);
}
}
void solve(int u,int fa){
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa || v == gson[u])
continue;
solve(v,u);
clear();
}
if(gson[u])
solve(gson[u],u);
t = u;
fans[u] = fans[gson[u]];
init(u,fa);
}
int main(){
scanf("%lld",&n);
for(int i = 1;i <= n;++i)
scanf("%d",&c[i]);
for(int i = 1;i < n;++i){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
solve(1,0);
for(int i = 1;i <= n;++i)
printf("%lld ",fans[i]);
}//该开ll就开ll,不开ll这题就被卡了