启发式合并及其应用
Posted aininot260
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了启发式合并及其应用相关的知识,希望对你有一定的参考价值。
所谓的启发式合并就是合并的时候把小的东西往大的东西里面一个一个插
这里顺便说一下之前刷过去的线段树合并
对于一个结点,如果两颗线段树都有此位置的结点,则直接合并两结点的信息
然后递归处理左右子树
若只有一个有,直接返回即可
最坏的情况要合并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 }
以上是关于启发式合并及其应用的主要内容,如果未能解决你的问题,请参考以下文章
bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)