【笔记】启发式合并
(1)本质:
类似并查集里面的按秩合并
每次合并加一个启发式的操作,就是每次将一个元素少的集合合并到元素多的集合里面,这样我们就可以把时间复杂度降到O(nlogn * op)
(2)例题:
luogu:梦幻布丁
题目地址:P3201 [HNOI2009] 梦幻布丁 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
注释与代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }