• 学习笔记——启发式合并


    我们要把N个集合,总共M个元素合并成一个大集合。

    很容易得出,最坏的情况下需要合并N次,每次合并M个元素,也就是O(MN)的时间复杂度。

    同样的问题,如果我们把小的往大的里合,是不是就快很多?

    那么他的复杂度是多少呢?

    考虑一个集合,被合并一次后他的大小至少乘2,那么他被合并的次数至多为(logn)次,对于集合内的每一个元素至多会合并(logn)次,那么总共会合并(O(nlogn))

    例题:loj dp一般看规律

    对于将一个元素换成另一种颜色的操作,不难抽象成把两个元素合并,这样最多会进行(logn)次有效操作

    启发式合并代码:

    void merge(int x,int y){
    //	cout<<"x = "<<x<<" "<<y<<endl;
    	bool flag = 0;
    	if (s[x].size() > s[y].size()) swap(x,y),flag = 1;
    	for (auto i = s[x].begin();i != s[x].end();i++){
    		auto lst = s[y].lower_bound(*i);
    		if (lst != s[y].end()) ans = min(ans,*lst-*i);
    		auto pre = s[y].lower_bound(*i);
    		if (pre != s[y].begin()) pre--,ans = min(ans,*i-*pre);
    	}
    //	cout<<"size = "<<s[x].size()<<" "<<s[y].size()<<endl;
    	for(auto i = s[x].begin();i != s[x].end();i++) s[y].insert(*i);
    	s[x].clear();
    	if (flag) swap(s[x],s[y]); 
    }
    

    约定x比y小,然后把x合并到y上,然后把小的清空,最终保证y是合并后的集合

    例题2:CF600E Lomsat gelral

    这道题我们可以遍历整棵树,并记录每种颜色出现几次

    但是每做完一棵子树就需要清空,以免对其兄弟造成影响。

    而这样做它的祖先时就要把它重新搜一遍,浪费时间

    但是我们发现,对于每个节点v,最后一棵子树是不用清空的,因为做完那棵子树后可 以把其结果直接加入v的答案中。

    选哪棵子树呢?当然是所含节点最多的一棵咯,我们称之为“重儿子”

    考虑一个点会被计算几次,他可以分为两个部分,dfs和暴力统计答案

    对于前者每个点只会被经过1次,对于后者,因为从一个点向上跳最多有log个轻边,所以会被经过(logn)次,这样n个点复杂度为(nlogn)

    代码:

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #define int long long
    #define B cout<<"Breakpoint"<<endl;
    #define O(x) cout<<#x<<" "<<x<<endl;
    #define o(x) cout<<#x<<" "<<x<<" ";
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e5+10;
    int n,col[maxn];
    struct node{
    	int to,nxt;
    }ed[maxn << 1];
    int head[maxn],tot;
    int sum,maxx;
    void add(int u,int to){
    	ed[++tot].to = to;
    	ed[tot].nxt = head[u];
    	head[u] = tot;
    }
    int siz[maxn],son[maxn];
    void dfs_init(int x,int fa){
    	siz[x] = 1;
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (to == fa) continue;
    		dfs_init(to,x);
    		siz[x] += siz[to];
    		if (siz[son[x]] < siz[to]) son[x] = to; 
    	}
    }	
    int c[maxn],ans[maxn];
    void getans(int x,int fa,int pos){
    	c[col[x]]++;
    	if (c[col[x]] > maxx) maxx = c[col[x]],sum = col[x];
    	else if (c[col[x]] == maxx) sum += col[x];
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (to == fa||to == pos) continue;
    		getans(to,x,pos);
    	}
    }	
    void clear(int x,int fa){
    	c[col[x]]--;
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (to == fa) continue;
    		clear(to,x);
    	}
    }
    void dfs(int x,int fa){
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (to == fa||to == son[x]) continue;
    		dfs(to,x),clear(to,x),sum = maxx = 0;
    	}
    	if (son[x]) dfs(son[x],x);
    	getans(x,fa,son[x]);
    	ans[x] = sum;
    }
    signed main(){
    	n = read();
    	for (int i = 1;i <= n;i++) col[i] = read();
    	for (int i = 1;i < n;i++){
    		int x = read(),y = read();
    		add(x,y),add(y,x);
    	}
    	dfs_init(1,0);dfs(1,0);
    	for (int i = 1;i <= n;i++) printf("%lld ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    python xlwt 设置单元格样式-合并单元格
    Ubuntu 16.04配置国内高速apt-get更新源
    python3.5 安装python3-tk
    m4a 转 wav
    hmm前后向算法
    hmm三个问题
    veterbi
    马尔科夫和隐马尔科夫
    Exception in thread "main" java.lang.UnsupportedClassVersionError: org/apache/ma ven/cli/Maven/java与javac版本不一致问题
    spring 配置定时任务
  • 原文地址:https://www.cnblogs.com/little-uu/p/14941460.html
Copyright © 2020-2023  润新知