【笔记】启发式合并

(1)本质:

  类似并查集里面的按秩合并
  每次合并加一个启发式的操作,就是每次将一个元素少的集合合并到元素多的集合里面,这样我们就可以把时间复杂度降到O(nlogn * op)

 

(2)例题:

luogu:梦幻布丁

题目地址:P3201 [HNOI2009] 梦幻布丁 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

注释与代码:

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+10,M=1e6+10; 
//颜色序号 col[i] <= M 

int n,m;                    //n个布丁,m次操作
int color[N],sz[M];            //color[i]表示第 
int f[M];                    //f[col]表示颜色col,实际上目前是链表f[col] 
int ans;                     //当前有多少段颜色 
int head[M],nx[M],st[M];    //st[i]是链表的末尾元素 

void merge(int x,int y) //x链表sz更小,选择将x链表全部合并到y链表 
{
    for(int i=head[x];i;i=nx[i])
    {
        //思考i的影响范围——前后两个元素,如果该元素前后是y颜色,则不同段颜色减少了
        //是z,则仍旧是不同段颜色
        //是x,则仍旧同段 
        if(color[i-1]==y ) ans--;
        if(color[i+1]==y ) ans--;
    }
    for(int i=head[x];i;i=nx[i])
        color[i]=y;
    //如果选择将x链表连到y链表末尾 
    nx[st[y]]=head[x];
    st[y]=st[x];
    //如果选择将x链表连到y链表头部 
//    nx[st[x]]=head[y];
//    head[y]=head[x];
    sz[y]+=sz[x];
}

void change(int x,int y)
{
    if(x==y ) return;            //这句话的作用是,去重 
    if(sz[f[x]] > sz[f[y]] )    //随意交换,其实1颜色变成2颜色,和2颜色变成1颜色效果一样 
        swap(f[x],f[y]);  
    
    if(!sz[f[x]] ) return;
    merge(f[x],f[y]);
    f[x]=0;                        //清空失效链表 
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&color[i]);
        if(color[i] != color[i-1] ) 
            ans++;
        
        if(!head[color[i]] )
            st[color[i]] = i;
        sz[color[i]]++;
        //在链表头部加入元素i 
        nx[i] = head[color[i]];
        head[color[i]] = i;
    }
    
    for(int i=1;i<=n;i++)
        f[color[i]]=color[i];
    
    while(m--)
    {
        int opt; 
        scanf("%d",&opt);
        
        if(opt == 2 ) printf("%d\n",ans);
        else
        {
            int x,y;
            scanf("%d%d",&x,&y);
            change(x,y);
        }
    }
    
    return 0;
} 
View Code

 

posted @ 2022-09-12 11:09  心若笺诗  阅读(15)  评论(0编辑  收藏  举报