bzoj2212(线段树合并第一道)

 

话说像这样的,维护的东西需要数据结构且需要合并的问题,就可以考虑合并。例如本题,我们所需要从叶子节点把维护的数据不断递推上来,所以就需要线段树合并。

肯定是动态开节点

 

这样的题如果写平衡树启发式合并的话,就要带两个log,而线段树合并一个log就可以,虽然每一次合并复杂度不确定,我们考虑,对于每一次操作,都是把两个线段树合并,

而整个叶子节点共n颗线段树,每一颗只有一个元素,显然无论如何合并,n-1次之后我们就能将他们完全合。整个过程的复杂度不会比空树中插入n个整数来的大。

 

线段树合并关键是结构相同,所以我们可以把这个思想套在trie上

 

至于空间是n log n的!证明?

#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
#define MK(a,b) make_pair((a),(b))
#define pii pair
using namespace std;
typedef long long ll;
const int N=200005;

inline int read()
{
	int ans,f=1;char ch;
	while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0';
	while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
	return ans*f;
}

int n,m,tot;
stack s;
struct aa
{
	int lc,rc,size;
}a[4000005];
int new_node()
{
	int u;
	if (!s.empty()) u=s.top(),s.pop();
	else u=++tot;
	a[u].lc=a[u].rc=a[u].size=0;
	return u; 
}

void insert(int &u,int l,int r,int val)
{
	if (u==0) u=new_node();a[u].size++;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (val<=mid) insert(a[u].lc,l,mid,val);
	else insert(a[u].rc,mid+1,r,val);
}
ll cnt0,cnt1;
int merge(int u1,int u2,int l,int r)
{
	if (u1==0) return u2;if (u2==0) return u1;
	cnt0+=(ll)a[a[u1].lc].size*a[a[u2].rc].size;//exchange
	cnt1+=(ll)a[a[u2].lc].size*a[a[u1].rc].size;//not exchange
	a[u1].size+=a[u2].size;
	if (l==r) {s.push(u2);return u1;}
	int mid=(l+r)>>1;
	a[u1].lc=merge(a[u1].lc,a[u2].lc,l,mid);
	a[u1].rc=merge(a[u1].rc,a[u2].rc,mid+1,r);
	s.push(u2);
	return u1;
}
pii work()
{
	int x=read();
	if (x!=0)
	{
		int tmp=0;
		insert(tmp,1,n,x);
		return MK(tmp,0);
	}
	pii tmpl=work(),tmpr=work();
	cnt0=cnt1=0;
	int rt=merge(tmpl.fi,tmpr.fi,1,n);
	return MK(rt,min(cnt0,cnt1)+tmpl.se+tmpr.se);
}

int main()
{
	n=read();
	pii ans=work();
	printf("%lld",ans.se);
	return 0;
}


 

你可能感兴趣的:(线段树)