• 树上启发式合并(DSU on Tree)


    树上启发式合并 ( DSU on Tree) 是一种优雅的暴力

    时间复杂度是 (O(nlogn))

    启发式就是基于直觉或经验的意思

    树上启发式合并的代码很简单

    void dfs(int u,int p,bool keep){
    	for(u.lightson v in u.son){
    		dfs(v,u,false);
    	}
    	if(have u.heavyson)dfs(u.heavyson,u,true);;
    	count();   //统计 u 及所有轻儿子的贡献,并计算 u 的答案
    	if(keep == false) del(); //如果不保留的话,删除记录
    }
    

    这段代码多看几遍,自己多想一想

    对于当前的节点 u,在执行 count() 计算节点 u 的答案的时候,其所有子节点的答案都已经被计算出来,并且只有 u 的重儿子及其子树的记录没有被删除,所以计算 u 的答案的时候只要暴力遍历所有的轻儿子及其子树就可以。

    极其暴力(bushi

    CF600E

    首先要明白怎么统计答案

    int mp[N],mx,sum; //mp是颜色出现次数,mx是最多出现的次数,sum是当前的最优节点的和
    
    mp[color]++;
    if (mp[color] > mx){
    	mx = mp[color];
    	sum = color;
    }
    else if (mp[color] == mx) sum += color;
    
    

    然后就是开始暴力合并(启发式)

    ll ans[N], sum, mx, mp[N];
    void count(int u, int p,int hson) { //暴力算轻儿子及子树的答案,此时重儿子及其子树的信息还在。
    	mp[color[u]]++;
    	if (mp[color[u]] > mx) {
    		mx = mp[color[u]];
    		sum = color[u];
    	}
    	else if (mp[color[u]] == mx) sum += color[u];
    
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p or v == hson)continue;
    		count(v, u, hson);
    	}
    }
    void del(int u, int p) { //删除子树的信息。
    	mp[color[u]]--;
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p)continue;
    		del(v, u);
    	}
    }
    void dfs(int u, int p, bool keep) {
    	
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p or v == son[u])continue;
    		dfs(v, u, false); //
    	}
    	if (son[u])dfs(son[u], u, true);
    	count(u, p, son[u]); ans[u] = sum;
    	if (not keep) {
    		sum = mx = 0;
    		del(u,p);
    	}
    }
    

    完整代码

    #include<bits/stdc++.h>
    #define repE(i,u) for(int i = head[u]; ~i;i = E[i].next)
    using namespace std;
    
    const int N = 1e5 + 10;
    typedef long long ll;
    int fa[N], sz[N], dep[N], son[N], color[N];
    struct Edge { int to, next; }E[N << 1];
    int head[N], tot;
    void addEdge(int from, int to) { E[tot] = { to,head[from] }; head[from] = tot++; }
    
    
    void dfs1(int u, int p) {
    	fa[u] = p;
    	dep[u] = dep[p] + 1;
    	sz[u] = 1;
    	int mx = -1;
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p)continue;
    		dfs1(v, u);
    		sz[u] += sz[v];
    		if (sz[v] > mx)mx = sz[v], son[u] = v;
    	}
    }
    
    ll ans[N], sum, mx, mp[N];
    void count(int u, int p,int hson) {
    	mp[color[u]]++;
    	if (mp[color[u]] > mx) {
    		mx = mp[color[u]];
    		sum = color[u];
    	}
    	else if (mp[color[u]] == mx) sum += color[u];
    
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p or v == hson)continue;
    		count(v, u, hson);
    	}
    }
    void del(int u, int p) {
    	mp[color[u]]--;
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p)continue;
    		del(v, u);
    	}
    }
    void dfs(int u, int p, bool keep) {
    
    	repE(i, u) {
    		int v = E[i].to;
    		if (v == p or v == son[u])continue;
    		dfs(v, u, false);
    	}
    	if (son[u])dfs(son[u], u, true);
    	count(u, p, son[u]); ans[u] = sum;
    	if (not keep) {
    		sum = mx = 0;
    		del(u,p);
    	}
    }
    
    int n;
    int main() {
        ios::sync_with_stdio(0);
    	cin >> n; memset(head, -1, sizeof head);
    	for (int i = 1; i <= n; i++)cin >> color[i];
    	for (int i = 1; i < n; i++) {
    		int a, b; cin >> a >> b;
    		addEdge(a, b); addEdge(b, a);
    	}
    	dfs1(1, 0);
    	dfs(1, 0, true);
    	for (int i = 1; i <= n; i++)cout << ans[i] << " 
    "[i == n];
    }
    
  • 相关阅读:
    一些易忘记的常识HTML,不定期添加
    base64 encoder/decoder for objectivec编码及解码(转)
    用XCode 开发基于网络库ACE的应用
    迅雷/旋风地址转换原理分析(转)
    主题:非常常用的Mac快捷键
    没事干测试ObjC数据类型
    These are the support and errata files for titles formerly published by Wrox Press Limited.
    iOS 开发者应该知道的 ARM 结构(转自apple4us)
    [创建型模式] Factory
    [创建型模式] Builder
  • 原文地址:https://www.cnblogs.com/sduwh/p/13900846.html
Copyright © 2020-2023  润新知