Codeforces 600E Lomsat gelral (启发式合并)

Codeforces 600E Lomsat gelral (启发式合并)

题目大意:
一棵树,每个点有一个颜色,每一次询问在u节点为根的子树中,颜色出现次数最多的那些颜色的和。
考虑启发式合并,每个节点开map,cnt[u][i]表示u节点为根的子树上,i颜色在出现多少次,sum[u][i]表示在当前子树中,恰好出现了i次的颜色和。
每次把小的插入到大的里边,修改cnt,sum
合并复杂度O(nlogn), map的复杂度O(logn), 总复杂度O(nlog2n)

#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
#define LL long long
#define N 100010
using namespace std;

struct Edge{
    int to, nxt;
}ed[N << 1];

int n, idc=0;
int head[N];
int col[N];
LL ans[N];
map<int, int> cnt[N];
map<int, LL> sum[N];

void adde(int u,int v){
    ed[++idc].to = v;
    ed[idc].nxt = head[u];
    head[u] = idc;
}

void dfs(int u,int fa){
    cnt[u].clear(); sum[u].clear();
    cnt[u][col[u]] = 1;
    sum[u][1] = col[u];
    for(int i=head[u]; i; i=ed[i].nxt){
        int v = ed[i].to;
        if(v == fa) continue;
        dfs(v, u);
        if(cnt[u].size() < cnt[v].size()){
            swap(cnt[u], cnt[v]);
            swap(sum[u], sum[v]);
        }
        for(map<int,int>::iterator it=cnt[v].begin(); it!=cnt[v].end(); it++){
            int x = it->first, y = it->second;
            if( cnt[u].count(x) ){
                sum[u][cnt[u][x]] -= x;
                cnt[u][x] += y;
                sum[u][cnt[u][x]] += x;
            }
            else{
                cnt[u][x] = y;
                sum[u][cnt[u][x]] += x;
            }
        }
    }
    map<int, LL>::iterator it = sum[u].end(); it--;
    ans[u] = it->second;//最大次数颜色和 
}

void init(){
    idc = 0;
    memset(head, 0, sizeof(head));
    memset(ed, 0, sizeof(ed));
}

int main(){
    while( scanf("%d", &n) != EOF ){
        init();
        for(int i=1; i<=n; i++) scanf("%d", &col[i]);
        for(int i=1; i<n; i++){
            int u, v; scanf("%d%d", &u, &v);
            adde(u, v); adde(v, u);
        }
        dfs(1, 1);
        for(int i=1; i<=n; i++) printf("%I64d ", ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值