[平衡树 启发式合并] BZOJ 1483 [HNOI2009]梦幻布丁

用set水过的

搬运下黄学长的复杂度分析吧,很有道理啊


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

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<set>
using namespace std;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x)
{
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

int n,ans;
int clr[100005],pos[1000005];
set<int> Set[1000005];
typedef set<int>::iterator ITER;

inline void Merge(int a,int b)
{
    for(ITER i=Set[a].begin();i!=Set[a].end();i++)
	{
		if(clr[*i-1]==b) ans--;
		if(clr[*i+1]==b) ans--;
		Set[b].insert(*i);
	}
	for(ITER i=Set[a].begin();i!=Set[a].end();i++) 
		clr[*i]=b;
	Set[a].clear();
}

int main()
{
	int order,a,b,Q;
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(n); read(Q);
	for(int i=1;i<=n;i++)
	{
		read(clr[i]); pos[clr[i]]=clr[i];
		if (clr[i]!=clr[i-1]) ans++;
		Set[clr[i]].insert(i);
	}
	while (Q--)
	{
		read(order);
		if(order==1)
		{
			read(a); read(b);
			if(a==b) continue;
			if(Set[pos[a]].size()>Set[pos[b]].size())
				swap(pos[a],pos[b]);
			Merge(pos[a],pos[b]);
		}
		else if (order==2)
			printf("%d\n",ans);
	}
	return 0;
}



你可能感兴趣的:([平衡树 启发式合并] BZOJ 1483 [HNOI2009]梦幻布丁)