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)\)

Orz 辰星凌 - 博客园 (cnblogs.com)

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;
}
posted @ 2022-02-13 11:28  _Famiglistimo  阅读(108)  评论(4编辑  收藏  举报