数据结构:启发式合并

所谓的启发式合并就是合并的时候把小的东西往大的东西里面一个一个插

这里顺便说一下之前刷过去的线段树合并

对于一个结点,如果两颗线段树都有此位置的结点,则直接合并两结点的信息

然后递归处理左右子树

若只有一个有,直接返回即可

最坏的情况要合并n个结点,然后每个结点合并的时间复杂度是O(logn)的,那么就是O(nlogn)的就好了

下面让我们看看均摊的情况:
1:每次O(N)
2:每次合并后,队列长度一定大于等于原来短的长度的两倍。
这样相当于每次合并都会让短的长度扩大一倍以上,
最多扩大logN次,所以总复杂度O(NlogN),每次O(logN)

这里给出一道启发式合并的简单题,BZOJ1483

N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色

 1 #include<cstdio>
 2 #include<set>
 3 using namespace std;
 4 int n,m,ans;
 5 int fa[1000005],v[100005];
 6 set<int> t[1000005];
 7 inline int read()
 8 {
 9     int x=0,f=1;char ch=getchar();
10     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
11     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
12     return x*f;
13 }
14 void solve(int a,int b)
15 {
16     for(set<int>::iterator i=t[a].begin();i!=t[a].end();i++)
17     {
18         if(v[*i-1]==b) ans--;
19         if(v[*i+1]==b) ans--;
20         t[b].insert(*i);
21     }
22     for(set<int>::iterator i=t[a].begin();i!=t[a].end();i++)
23         v[*i]=b;
24     t[a].clear();
25     
26 }
27 int main()
28 {
29     n=read();m=read();
30     for(int i=1;i<=n;i++) v[i]=read();
31     for(int i=1;i<=n;i++)
32     {
33         fa[v[i]]=v[i];
34         if(v[i]!=v[i-1]) ans++;  //有颜色间隔
35         t[v[i]].insert(i);  //当前下标扔对应颜色的框里 
36     }
37     for(int i=1;i<=m;i++)
38     {
39         int f=read(),a,b;
40         if(f==2) printf("%d
",ans);
41         else
42         {
43             a=read();b=read();
44             if(a==b) continue;
45             if(t[fa[a]].size()>t[fa[b]].size())
46                 swap(fa[a],fa[b]);
47             a=fa[a];b=fa[b];
48             solve(a,b);
49         }
50     } 
51     return 0;
52 }
原文地址:https://www.cnblogs.com/aininot260/p/9531766.html