链接:CF600E Lomsat gelral - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:
题解:对于任意一个点都要求出整个子树内最多出现次数的数字和,可以使用启发式式合并来解决这道题,当然也可以使用线段树合并来解决。
前置芝士
动态开点,权值线段树
线段树合并
线段树合并主要是将两棵动态开点的权值线段树进行合并。假设当前合并的线段树为左,右线段树,最后将右线段树合并到左线段树上。合并方式是左线段树或者右线段树为空树时,直接将两棵树的或值给左线段树即可。当两棵树均为非空时,说明都需要继续按左右子树递归合并,直到叶子节点时可以直接赋值结束。虽然看着很不靠谱,但复杂度是 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)) 的。
实现
了解了线段树合并之后,容易发现对于一个父节点是可以将子节点的线段树直接合并上来的,那么可以先计算子节点再计算父节点。对于线段树上维护的,应该是最多出现次数,及其和。因为是权值线段树,则合并时底层各个权值的出现次数已经累加完,要计算的是最多出现次数及其和。当线段树上的左右子树最大出现次数不一样时,取大的那边,一样时则可以把数字和累加,最后每个节点的答案即为该点线段树头节点的数字和。
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using ll=long long;
using P=pair<int,int>;
const ll inf=1e18;
struct Merge{
static constexpr int N=1e5+5;
int n,now;
vector<int>t,ls,rs,mx,va,rt;
vector<ll>sm;
Merge(int x=N):n(x+5<<5),t(n),ls(n),rs(n),mx(n),sm(n),va(n),rt(x+5),now(0){}
void pushup(int k){
int l=ls[k],r=rs[k];
if(mx[l]>mx[r])
{
sm[k]=sm[l];
mx[k]=mx[l];
va[k]=va[l];
}
else if(mx[l]<mx[r])
{
sm[k]=sm[r];
mx[k]=mx[r];
va[k]=va[r];
}
else
{
sm[k]=sm[l]+sm[r];
mx[k]=mx[l];
va[k]=va[l];
}
}
void update(int&u,int l,int r,int pos){
if(!u)u=++now;
if(l==r)
{
sm[u]+=l;
mx[u]++;
va[u]=l;
return;
}
int mid=l+r>>1;
if(pos<=mid)update(ls[u],l,mid,pos);
else update(rs[u],mid+1,r,pos);
pushup(u);
}
void merge(int&u,int&v,int l,int r){
if(!u||!v){u=u|v; return;}
if(l==r)
{
va[u]=sm[u]=l;
mx[u]+=mx[v];
return;
}
int mid=l+r>>1;
merge(ls[u],ls[v],l,mid);
merge(rs[u],rs[v],mid+1,r);
pushup(u);
}
};
void solve()
{
int n; cin>>n;
vector<int>a(n+1);
vector<vector<int>>ed(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1,u,v;i<n;i++)
{
cin>>u>>v;
ed[u].push_back(v);
ed[v].push_back(u);
}
Merge tr(n);
vector<ll>ans(n+1);
auto dfs=[&](auto dfs,int x,int fa)->void{
tr.update(tr.rt[x],1,n,a[x]);
for(auto y:ed[x])
{
if(y==fa)continue;
dfs(dfs,y,x);
tr.merge(tr.rt[x],tr.rt[y],1,n);
}
ans[x]=tr.sm[tr.rt[x]];
};
dfs(dfs,1,0);
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<" \n"[i==n];
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t=1; //cin>>t;
while(t--)solve();
return 0;
}