启发式合并

启发式合并

启发式合并就是每次把小的集合丢到大的集合中

这样子小的 sz 至少是变成两倍的, 所以每个元素最多被遍历 log 次, 因此复杂度是 O(nlogn)的

[ABC329F] Colored Ball

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 2e5 + 100;

set<int> s[N];

void solve() {
    int n, q; cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        int x; cin >> x;
        s[i].insert(x);
    }
    while (q--) {    
        int l, r; cin >> l >> r;
        if (s[l].size() > s[r].size()) swap(s[l], s[r]);
        while (s[l].size()) {
            s[r].insert(*s[l].begin());
            s[l].erase(s[l].begin());
        }
        cout << (int)s[r].size() << endl;
    }
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve();
    return 0;
}
View Code
复制代码

树上启发式合并

树上启发式合并通过树链剖分优化时间复杂度

重链剖分复杂度是 O(nlogn)

长链剖分复杂度是 O(n)

Lomsat gelral

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

// 树上启发式合并是离线的

const int N = 1e5 + 100;

int col[N], cnt[N], res[N], ans, maxn;
vector<int> g[N];

void update(int x, int v) {
    cnt[col[x]] += v;
    if (cnt[col[x]] > maxn) maxn = cnt[col[x]], ans = col[x];
    else if (cnt[col[x]] == maxn) ans += col[x];
}

// 链剖不求top数组(链顶上面的点)版本启发式合并
int sz[N], son[N], L[N], R[N], id[N], dfn;
void Dfs(int x, int fa) {
    sz[x] = 1; L[x] = ++dfn; id[dfn] = x;
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
    R[x] = dfn;
}

// del 表示是否要 init 当前信息
// 启发式合并中我们在每条链顶部节点 init
void DFS(int x, int fa, int del) {
    for (auto y : g[x]) {
        if (y == son[x] || y == fa) continue;
        // 每个点的轻儿子都是链最顶部点
        DFS(y, x, 1);
    }
    // 先走轻儿子, 省掉重儿子清空时间
    if (son[x]) DFS(son[x], x, 0);
    for (auto y : g[x]) {
        if (y == son[x] || y == fa) continue;
        // 对轻儿子子树暴力计算
        for (int i = L[y]; i <= R[y]; i++) update(id[i], 1);   
    }
    // 加上该点贡献
    update(x, 1);
    res[x] = ans;

    // init
    if (del) {
        for (int i = L[x]; i <= R[x]; i++) update(id[i], -1);
        maxn = ans = 0;
    }
}

void solve() {
    int n; cin >> n;
    for (int i = 1; i <= n; i++) cin >> col[i];
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }   
    Dfs(1, 0);
    DFS(1, 0, 1);
    for (int i = 1; i <= n; i++) cout << res[i] << ' ';
    cout << endl;
}   

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();    

    return 0;
}
View Code
复制代码

 Blood Cousins Return

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 4e5 + 100;

int v[N], root[N], id[N], ans[N], maxdep;
vector<int> g[N];
map<string, int> mp;
vector<array<int, 2>> que[N];
set<int> s[N];

// 链剖不求top数组(链顶上面的点)版本启发式合并
int sz[N], son[N], L[N], R[N], dep[N], rev[N], dfn;
void Dfs(int x, int fa) {
    sz[x] = 1; L[x] = ++dfn;
    dep[x] = dep[fa] + 1; rev[dfn] = x;
    maxdep = max(maxdep, dep[x]);
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
    R[x] = dfn;
}

void DFS(int x, int fa, int del) {
    for (auto y : g[x]) {
        if(y == fa || y == son[x]) continue;
        DFS(y, x, 1);
    }
    if (son[x]) DFS(son[x], x, 0);
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        for (int i = L[y]; i <= R[y]; i++) s[dep[rev[i]]].insert(id[rev[i]]);
    }
    s[dep[x]].insert(id[x]);
    for (auto [k, id] : que[x]) ans[id] = s[dep[x] + k].size();
    if (del) {
        for (int i = 1; i <= maxdep; i++) s[i].clear();
    }
}

void solve() {
    int n; cin >> n;
    int nowid = 0, cntroot = 0;
    for (int i = 1; i <= n; i++) {
        string s; cin >> s;
        int fa; cin >> fa;
        if (!mp[s]) mp[s] = ++nowid;
        id[i] = mp[s];
        if (!fa) {
            ++cntroot;
            root[cntroot] = i; 
        } else {
            g[i].push_back(fa);
            g[fa].push_back(i);
        }
    }
    int q; cin >> q;
    for (int i = 1; i <= q; i++) {
        int v, k; cin >> v >> k;
        que[v].push_back({k, i});
    }
    for (int i = 1; i <= cntroot; i++) Dfs(root[i], 0);
    for (int i = 1; i <= cntroot; i++) DFS(root[i], 0, 1);
    for (int i = 1; i <= q; i++) cout << ans[i] << endl;
}   

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();    

    return 0;
}
View Code
复制代码

 


 

这两题处理贡献方式基本上一模一样

Count Paths

  • 启发式合并做法
复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

const int N = 2e5 + 100;

int col[N], ans;
vector<int> g[N];
map<int, int> cnt[N];

void dfs(int x, int fa) {
    cnt[x][col[x]]++;
    for (auto y : g[x]) {
        if (y == fa) continue;
        dfs(y, x);
        if (cnt[y].count(col[x])) {
            ans += cnt[y][col[x]];
            cnt[y].erase(col[x]);
        }
        if (cnt[y].size() > cnt[x].size()) cnt[x].swap(cnt[y]);
        for (auto &[i, j] : cnt[y]) {
            if (cnt[x].count(i)) ans += cnt[x][i] * j;
            cnt[x][i] += j;
        }
    }   
}

void solve() {
    int n; cin >> n;
    for (int i = 0; i <= n; i++) g[i].clear(), cnt[i].clear();
    for (int i = 1; i <= n; i++) cin >> col[i];
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    } 
    ans = 0;
    dfs(1, 1);
    cout << ans << endl;
}    

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int T = 1; cin >> T;
    while (T--) solve();
    // solve();    

    return 0;
}
View Code
复制代码
  • 树上启发式合并做法
复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

const int N = 4e5 + 100;

vector<int> g[N];
int col[N], vis[N], ans, now;
map<int, int> cnt[N];

// 链剖不求top数组(链顶上面的点)版本启发式合并
int sz[N], son[N];
void Dfs(int x, int fa) {
    sz[x] = 1; son[x] = 0;
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
}

void calc1(int x, int fa) {
    if (vis[col[x]] == 0) {
        if (col[x] != col[now]) ans += cnt[now][col[x]];
    }
    vis[col[x]]++;
    for (auto y : g[x]) {
        if (y == fa) continue;
        calc1(y, x);
    }
    vis[col[x]]--;
}

void calc2(int x, int fa) {
    if (vis[col[x]] == 0) {
        cnt[now][col[x]]++;
    }
    vis[col[x]]++;
    for (auto y : g[x]) {
        if (y == fa) continue;
        calc2(y, x);
    }
    vis[col[x]]--;
}

// del 表示是否要 init 当前信息
// 启发式合并中我们在每条链顶部节点 init
void DFS(int x, int fa, int del) {
    for (auto y : g[x]) {
        if (y == son[x] || y == fa) continue;
        // 每个点的轻儿子都是链最顶部点
        DFS(y, x, 1);
    }
    // 先走轻儿子, 重儿子保留信息, 省掉重儿子清空时间
    if (son[x]) DFS(son[x], x, 0);
    now = x;
    for (auto y : g[x]) {
        if (y == son[x] || y == fa) continue;
        // 对轻儿子子树计算贡献
        calc1(y, x);
        calc2(y, x);
    }
    // 加上该点贡献
    ans += cnt[x][col[x]];
    cnt[x][col[x]] = 1;
    // 清空贡献
    if (!del) swap(cnt[fa], cnt[x]);
    cnt[x].clear();
}

void solve() {
    int n; cin >> n;
    ans = 0;
    for (int i = 0; i <= n; i++) g[i].clear(), cnt[i].clear(), vis[i] = sz[i] = son[i] = 0;
    for (int i = 1; i <= n; i++) cin >> col[i];
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }        
    Dfs(1, 1); DFS(1, 1, 1);
    cout << ans << endl;
}   

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int T = 1; cin >> T;
    while (T--) solve();
    // solve();

    return 0;
}
View Code
复制代码

E-小睿睿的伤害

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

// 处理每个 val 可以提供的因子数( 1e5 内的数最多的也就 100 个)
// 把每个子树能提供的因子拿桶记录下来, 如果桶重对应值大于 2 就是可能作为答案的值

const int N = 1e5 + 100;

int val[N];
vector<int> g[N], fac[N];
array<int, 2> ans[N];

int sz[N], son[N], L[N], R[N], pos[N], dfn;
void Dfs(int x, int fa) {
    L[x] = ++dfn; sz[x] = 1;
    pos[dfn] = x;
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
    R[x] = dfn;
}

int vis[N], maxn, cnt;
void calc1(int x) {
    for (int i = L[x]; i <= R[x]; i++) {
        for (auto j : fac[pos[i]]) {
            if (vis[j] && j > maxn) maxn = j, cnt = vis[j];
            else if (j == maxn) cnt += vis[j];                
        }
    }
}

void calc2(int x) {
    for (int i = L[x]; i <= R[x]; i++) {
        for (auto j : fac[pos[i]]) {
            vis[j]++;
        }
    }
}

void DFS(int x, int fa, int del) {
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        DFS(y, x, 1);
    }
    if (son[x]) DFS(son[x], x, 0);
    maxn = cnt = 0;
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        calc1(y); calc2(y);
    }
    for (auto j : fac[x]) {
        if (!vis[j]) continue;
        if (vis[j] && j > maxn) maxn = j, cnt = vis[j];
        else if (j == maxn) cnt += vis[j];
    }
    for (auto j : fac[x]) vis[j]++;
    ans[x] = {maxn, cnt};
    if (del) {
        for (int i = L[x]; i <= R[x]; i++) {
            for (auto j : fac[pos[i]]) vis[j]--;
        }
    }
} 

void solve() {
    int n; cin >> n;
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    for (int i = 1; i <= n; i++) cin >> val[i];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j * j <= val[i]; j++) {
            if (val[i] % j == 0) {
                fac[i].push_back(j);
                if (val[i] / j != j) fac[i].push_back(val[i] / j);
            }
        }
    }
    Dfs(1, 1); DFS(1, 1, 1);
    for (int i = 1; i <= n; i++) {
        auto [x, y] = ans[i];
        cout << x << ' ' << y << endl;
    }
}   

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();    

    return 0;
}
View Code
复制代码

 


 

智乃酱的子树查询类问题

长链剖分, 线段树维护

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
typedef long long ll;

const int N = 2e5 + 100;

int val[N], sz;
vector<int> g[N];
vector<array<int, 3>> que[N];

struct node {
    int maxn, minn, sum;
} Seg[N * 4];

node pushup(node l, node r) {
    node ans; ans.sum = l.sum + r.sum;
    ans.maxn = max(l.maxn, r.maxn);
    ans.minn = min(l.minn, r.minn);
    return ans;
}

void pushup(int id) {
    Seg[id] = pushup(Seg[id * 2], Seg[id * 2 + 1]);
}

void build(int id, int l, int r) {
    if (l == r) {
        Seg[id] = {LLONG_MIN / 2, LLONG_MAX / 2, 0};
        return;
    }
    int mid = l + r >> 1;
    build(id * 2, l, mid); build(id * 2 + 1, mid + 1, r);
    pushup(id);
}

void modify(int id, int l, int r, int pos, node v) {
    if (l == r) {
        Seg[id].maxn = max(Seg[id].maxn, v.maxn);
        Seg[id].minn = min(Seg[id].minn, v.minn);
        Seg[id].sum += v.sum;
        return;
    } 
    int mid = l + r >> 1;
    if (pos <= mid) modify(id * 2, l, mid, pos, v);
    else modify(id * 2 + 1, mid + 1, r, pos, v);
    pushup(id);
}

node query(int id, int l, int r, int x, int y) {
    if (x <= l && y >= r) return Seg[id];
    int mid = l + r >> 1;
    node ans = {LLONG_MIN / 2, LLONG_MAX / 2, 0};
    if (x <= mid) ans = pushup(ans, query(id * 2, l, mid, x, y));
    if (y > mid) ans = pushup(ans, query(id * 2 + 1, mid + 1, r, x, y));
    return ans;
}

int son[N], len[N], L[N], R[N], pos[N], dfn;    
void Dfs(int x, int fa) {
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        if (len[son[x]] < len[y]) son[x] = y;
    }
    len[x] = len[son[x]] + 1;
}

void DFS(int x, int fa) {
    L[x] = ++dfn; pos[dfn] = x;
    R[x] = L[x] + len[x] - 1;
    if (son[x]) DFS(son[x], x);
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        DFS(y, x);
    }
}

node ans[N];
void DSU(int x, int fa) {
    if (son[x]) DSU(son[x], x);
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        DSU(y, x);
        // 轻(短)链信息全合并到(长)重链上了???
        for (int i = L[y], dep = 1; i <= R[y]; i++, dep++) modify(1, 1, sz, L[x] + dep, query(1, 1, sz, i, i));
    } 
    modify(1, 1, sz, L[x], {val[x], val[x], val[x]});
    for (auto [l, r, i] : que[x]) ans[i] = query(1, 1, sz, L[x] + l, L[x] + r);
}

void solve() {
    int n; cin >> n;
    sz = N / 2;
    for (int i = 1; i <= n; i++) cin >> val[i];
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    int q; cin >> q;
    for (int i = 1; i <= q; i++) {
        int x, l, r; cin >> x >> l >> r;
        que[x].push_back({l, r, i});
    }
    build(1, 1, sz);
    Dfs(1, 1); DFS(1, 1); DSU(1, 1);
    for (int i = 1; i <= q; i++) {
        auto [a, b, c] = ans[i];
        cout << b << ' ' << a << ' ' << c << endl;
    }
}   

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();    

    return 0;
}
View Code
复制代码

正经人谁吃泡菜肥牛啊?

重链剖分,树状数组维护

复制代码
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 1e6 + 100;

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_) {
        n = n_;
        for(int i = 0; i <= n; i++) tr[i] = 0;
    }
    int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x) {
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    T sum(int l, int r) {return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);}
};

int a[N], ans[N], m;
vector<int> g[N];
vector<array<int, 2>> que[N];
BIT<int> T;

int sz[N], son[N], L[N], R[N], pos[N], dfn;
void Dfs(int x, int fa) {
    sz[x] = 1; L[x] = ++ dfn;
    pos[dfn] = x;
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
    R[x] = dfn;
}

void DFS(int x, int fa, int del) {
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        DFS(y, x, 1);
    }
    if (son[x]) DFS(son[x], x, 0);
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        for (int i = L[y]; i <= R[y]; i++) T.add(a[pos[i]], m, 1);
    }
    T.add(a[x], m, 1);
    for (auto [k, id] : que[x]) ans[id] = T.sum(k);
    if (del) {
        for (int i = L[x]; i <= R[x]; i++) T.add(a[pos[i]], m, -1);
    }
}

void solve() {
    int n; cin >> n;
    m = N - 100;
    T.init(m);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    int q; cin >> q;
    for (int i = 1; i <= q; i++) {
        int x, k; cin >> x >> k;
        que[x].push_back({k, i});
    }
    Dfs(1, 1); DFS(1, 1, 1);
    for (int i = 1; i <= q; i++) cout << ans[i] << endl;
}    

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    // int T = 1; cin >> T;
    // while (T--) solve();
    solve();    

    return 0;
}
View Code
复制代码

DongDong数颜色

重链剖分,比较模板的一题

复制代码
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 1e5 + 100;

int ans, col[N], res[N], vis[N];
vector<int> g[N];

void update(int x, int v) {
    if (vis[x] == 0) {
        if (v > 0) ans++;
        else ans--;
    }
    vis[x] += v;
}

int L[N], R[N], sz[N], son[N], pos[N], dfn;
void Dfs(int x, int fa) {
    sz[x] = 1; L[x] = ++dfn;
    pos[dfn] = x;
    for (auto y : g[x]) {
        if (y == fa) continue;
        Dfs(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
    R[x] = dfn;
}

void DFS(int x, int fa, int del) {
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        DFS(y, x, 1);
    }
    if (son[x]) DFS(son[x], x, 0);
    for (auto y : g[x]) {
        if (y == fa || y == son[x]) continue;
        for (int i = L[y]; i <= R[y]; i++) update(col[pos[i]], 1); 
    }
    update(col[x], 1);
    res[x] = ans;
    if (del) {
        for (int i = L[x]; i <= R[x]; i++) update(col[pos[i]], -1);
        ans = 0;
    }
}

void solve() {
    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> col[i];
    for (int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    Dfs(1, 1); DFS(1, 1, 1);
    while (m--) {
        int x; cin >> x;
        cout << res[x] << endl;
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve();
    return 0;
}
View Code
复制代码

 

posted @   zhujio  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示