树上启发式合并

树上启发式合并,是在统计信息的复杂度较高,没有办法每个点开一个信息空间的时候,考虑将信息空间公用,同时尽量保持时空复杂度平衡的方法。

例题:

CF600E

给定一棵有根树,每个点有一个颜色。对于每一个点为根的子树,求子树内出现次数最多的颜色的和(可并列)。
\(n \le 10^5, c_i \le n\)

思路分析:

https://zhuanlan.zhihu.com/p/565967113
时间复杂度 \(O(n \log n)\)

代码实现:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int n; 
int hson[100010];
int c[100010];
vector<int> g[100010];
int sz[100010];
int ans[100010], maxt, cnt[100010];
void fhson(int now, int fa) {
    sz[now] = 1;
    int mx = 0;
    f(i, 0, (int)g[now].size() - 1) {
        if(g[now][i] != fa) {
            fhson(g[now][i], now);
            sz[now] += sz[g[now][i]];
            if(sz[g[now][i]] > mx) {
                mx = sz[g[now][i]];
                hson[now] = g[now][i];
            }
        }
    }
}
int sum = 0;
void add(int now) {
    cnt[c[now]]++;
    if(cnt[c[now]] > maxt) {
        maxt = cnt[c[now]];
        sum = c[now];
    }
    else if(cnt[c[now]] == maxt){
        sum += c[now];
    }
}
void del(int now) {
    --cnt[c[now]];
    sum = 0; maxt = 0;
}
void asubtree(int now, int fa) {
    add(now);
    f(i, 0, (int)g[now].size()- 1) {
        if(g[now][i] != fa) asubtree(g[now][i], now);
    }
    return;
}
void dsubtree(int now, int fa) {
    del(now);
    f(i, 0, (int)g[now].size()- 1) {
        if(g[now][i] != fa) dsubtree(g[now][i], now);
    }
    return;    
}
void dfs(int now, int fa, bool keep) {
    f(i, 0, (int)g[now].size()- 1) {
        if(g[now][i] == fa ) continue;
        if(g[now][i] == hson[now]) continue;
        dfs(g[now][i], now, 0);
    }
    if(hson[now])dfs(hson[now], now, 1);
    add(now);
    f(i, 0, (int) g[now].size() - 1) {
        if(g[now][i] == fa ) continue;
        if(g[now][i] == hson[now]) continue;
        asubtree(g[now][i], now);        
    }
    ans[now] = sum;
    if(!keep) {
        dsubtree(now, fa);

    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    cin >> n;
    f(i, 1, n) {
        cin >> c[i];
    }
    f(i, 1, n - 1) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);g[v].push_back(u);
    }
    fhson(1, 0);
    dfs(1, 0, 0);
    f(i,1 ,n) cout << ans[i] << " ";
    time_t finish = clock();
    //cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}

细节:

  • 分析一下发现 del 函数一旦运行,一定是把整个信息删空了。这时只需要直接把 \(sum\)\(maxt\) 直接初始化即可。
posted @ 2022-10-07 20:59  OIer某罗  阅读(25)  评论(0编辑  收藏  举报