码迷,mamicode.com
首页 > 其他好文 > 详细

启发式合并

时间:2015-01-26 15:05:29      阅读:234      评论:0      收藏:0      [点我收藏+]

标签:

启发式合并就是一个简单的思想, 就是每次在合并的时候 都把 小的合并到大的里面。 可以证明这个方法在很多时候都可以把合并次数 为 n 的问题优化到 logn次。

应用

一) 并查集

并查集的按秩合并就是记录一下每个点的size 是多大然后每次 把 size 较小的那个接到较大的上面。 但是好像并不是很常用?

二) 链表

bzoj 1483: [HNOI2009]梦幻布丁 

一个序列上有一些颜色, 每次把 颜色 为a 的都涂成 颜色 b, 询问当前的序列中有多少块颜色。

对于每一种颜色建一个链表存这个颜色覆盖的所有位置, 每次修改把 a 链表上的所有点都改成 b, 并且把 a 接到b 上面。复杂度是 n2 的。

用启发式合并优化就是每次都改较小的那个, 脑补一下的确可以做到 nlogn。

实现的时候因为一次覆盖操作后 a 和 b 原本的两种颜色都变成了一种, 所以记录一下每种颜色当前代表的是什么颜色就可以了。显然在合并前的序列中每出现一组相邻的 a, b, 就会对答案产生 -1 的贡献, 而无论是把 a 都变成 b , 还是把 b 都变成a, 不会影响对这个贡献的统计。 修改颜色的时候顺便统计一下就好了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXM 1000005
using namespace std;
int n, m, c[MAXM], head[MAXM], fact[MAXM], st[MAXM], next[MAXM], s[MAXM], ans;
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++){
        scanf("%d", &c[i]); 
        if(c[i] != c[i - 1])ans ++;
        s[c[i]] ++;
        if(!head[c[i]]) st[c[i]] = i;
        next[i] = head[c[i]]; head[c[i]] = i;
        fact[c[i]] = c[i];
    }
    while(m --){
        int k; scanf("%d", &k);
        if(k == 2)printf("%d\n", ans);
        else{
            int a, b; scanf("%d%d", &a, &b);
            if(a == b)continue;
            if(s[fact[b]] < s[fact[a]])swap(fact[b], fact[a]);
            a = fact[a], b = fact[b];
            if(!s[a])continue;
            s[b] += s[a]; s[a] = 0;
            for(int i = head[a]; i; i = next[i]){
                if(c[i - 1] == b)ans --;
                if(c[i + 1] == b)ans --;    
            }
            for(int i = head[a]; i; i = next[i]) c[i] = b;
            next[st[b]] = head[a]; st[b] = st[a];
            head[a] = st[a] = 0;
        }    
    }
    return 0;
}

 

启发式合并

标签:

原文地址:http://www.cnblogs.com/lixintong911/p/4250222.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!