CF600E Lomsat gelral 题解
Statement
CF600E Lomsat gelral - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
Solution
第一眼线段树合并,想到有点码量,直接看题解
题解大部分都是 DSU on Tree,突然发现了 CF600E - 辰星凌 - 博客园 (cnblogs.com) 这样一篇神奇思路用的 DFS 序+分治 ,给出了另外一个统计子树信息的思路
有结论:
设 \(dfn[i′]<dfn[i]\),若存在一个 \(dfn[j]\) 满足 \(dfn[i]⩽dfn[j]⩽dfn[i]+siz[i]-1\) 且 \(dfn[i′]⩽dfn[j]⩽dfn[i^{\prime}]+siz[i^{\prime}]-1\),那么 一定有 \(i^{\prime}\) 是 \(i\) 的祖先
我们暴力的想法是对于一个点 \(i\) ,暴力统计以下所有 \(j\) 满足 \(dfn[i]\le dfn[j]\le dfn[i]+siz[i]-1\) ,即子树中的点的信息
我们发现这样很浪费,因为很多信息被反复加入又删除
借助上面的结论,我们知道了其实对于满足 \(dfn[i]\le dfn[j]\le dfn[i]+siz[i]-1\) 的点之间有某种关系,比如 \(i^{\prime}\) 可以把 \(i\) 的信息全部嫖了
所以我们考虑分治 \(dfs\) 序
对于当前分治到的 \(dfs\) 序区间 \((l,r)\),我们把 \(mid\) 看作上面结论中的 \(dfn[j]\) ,从 \(mid\) 倒序枚举到 \(l\) ,我们可以得到所有 \(ans[i]\) 满足 \(l\le dfn[i]\le mid\) 且 \(mid<dfn[i]+siz[i]-1\le r\) (具体看代码)
复杂度 \(O(n\log n)\)
Code
#include<bits/stdc++.h>
#define int long long
#define mid ((l+r)>>1)
using namespace std;
const int N = 1e5+5;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
int c[N],siz[N],dfn[N],rev[N],ans[N],cnt[N],tmp[N];
vector<int>Edge[N];
int n,tim,top,as,mx;
void dfs(int u,int fath){
siz[u]=1,rev[dfn[u]=++tim]=u;
for(auto v:Edge[u])if(v^fath)
dfs(v,u),siz[u]+=siz[v];
}
void clear(){while(top)cnt[tmp[top--]]=0;as=mx=0;}
void insert(int u){
++cnt[tmp[++top]=c[u]];
if(cnt[c[u]]>mx)mx=cnt[c[u]],as=c[u];
else if(cnt[c[u]]==mx)as+=c[u];
}
void solve(int l,int r){
if(l==r){
if(siz[rev[l]]==1)
ans[rev[l]]=c[rev[l]];
return ;
}
solve(l,mid),solve(mid+1,r),clear();
for(int i=mid,p=mid,j;j=i+siz[rev[i]]-1,i>=l&&j<=r;--i){
//注意这里的 j 和上面所述的 j 的意义不大一样
insert(rev[i]);//时刻注意我们的 i 代表的是一个 dfn 值
if(j<=mid)continue;
while(p<j)insert(rev[++p]);//加入子树内的点
ans[rev[i]]=as;
}
}
signed main(){
n=read();
for(int i=1;i<=n;++i)c[i]=read();
for(int i=1,u,v;i<n;++i)
u=read(),v=read(),
Edge[u].emplace_back(v),
Edge[v].emplace_back(u);
dfs(1,0),solve(1,n);
for(int i=1;i<=n;++i)
printf("%lld ",ans[i]);
return 0;
}