所謂的啟發式合並就是合並的時候把小的東西往大的東西里面一個一個插
這里順便說一下之前刷過去的線段樹合並
對於一個結點,如果兩顆線段樹都有此位置的結點,則直接合並兩結點的信息
然后遞歸處理左右子樹
若只有一個有,直接返回即可
最壞的情況要合並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\n",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 }
