E. Lomsat gelral

原题链接

题解

暴力:遍历所有点为根节点的情况,然后遍历子节点,统计众数,时间复杂度 O(n2)

优化:上面的算法时间复杂度之所以为 O(n2) 是因为算父节点时,子节点又重新算了一遍,所以我们可以在算父节点时,保留一个子树的贡献,然后其他子树的贡献暴力遍历一遍
运用重链剖分,每次保留重儿子所在的子树的贡献,遍历轻儿子所在子树所有点的贡献,时间复杂度来到了 O(nlogn)

证明:

令轻儿子的“上边”为轻边,则每有一个轻边,都要遍历一遍其子树内的所有元素
所以节点 u 被暴力计算贡献的次数为根节点到 u 的路径上,轻边的个数
因为轻边所在子树大小小于父节点所在子树大小的一半,所以根节点到任意节点的路径上,轻边数量不超过 logn

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll dfn[100005];
ll sizes[100005];
vector<ll> G[100005];
ll bigson[100005];//代表编号
ll cnt=0;

ll haxi[1000005];

void dfs1(ll now,ll fa)
{
    dfn[now]=++cnt;
    haxi[cnt]=now;
    sizes[now]=1;
    ll maxs=0;

    for(auto next:G[now])
    {
        if(next==fa) continue;
        dfs1(next,now);
        sizes[now]+=sizes[next];
        if(sizes[next]>maxs)
        {
            bigson[now]=next;
            maxs=sizes[next];
        }
    }
}


ll tot=0;//出现不同颜色数
ll line=0;//最大出现次数
ll ans=0;//贡献
ll sum[100005]={0};//维护每个颜色出现的次数
ll st[100005]={0};//维护出现过的颜色的编号,算是离散化

void cancel()
{
    while(tot) sum[st[tot--]]=0;
    line=0;
    ans=0;
}

ll c[100005];
ll res[1000005];

void add(ll id)
{
    if(!sum[c[id]]) st[++tot]=c[id];

    sum[c[id]]++;
    if(sum[c[id]]>line)
    {
        ans=c[id];
        line=sum[c[id]];
    }
    else if(sum[c[id]]==line) ans+=c[id];//由于颜色出现统计只有清零和增加两种,所以ans要么变大要么为零
}

void dfs2(ll now)
{
    for(auto next:G[now])
    {
        if(dfn[next]<dfn[now]||next==bigson[now]) continue;

        dfs2(next);
        cancel();//取消其贡献。为什么要取消?为了清空计算其他子树
    }

    if(bigson[now])
    {
        dfs2(bigson[now]);
    }


    for(auto next:G[now])
    {
        if(dfn[next]<dfn[now]||next==bigson[now]) continue;

        for(ll i=dfn[next];i<=dfn[next]+sizes[next]-1;i++)
        {
            add(haxi[i]);//加上其贡献
        }
    }

    add(now);
    res[now]=ans;
}

void solve()
{
    ll n;
    cin>>n;

    for(ll i=1;i<=n;i++) cin>>c[i];

    for(ll i=1;i<n;i++)
    {
        ll x,y;
        cin>>x>>y;

        G[x].push_back(y);
        G[y].push_back(x);
    }

    dfs1(1,1);
    dfs2(1);

    for(ll i=1;i<=n;i++) cout<<res[i]<<' ';
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    ll t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}

posted @   纯粹的  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示