1. 程式人生 > >bzoj2212(線段樹合併第一道)

bzoj2212(線段樹合併第一道)

話說像這樣的,維護的東西需要資料結構且需要合併的問題,就可以考慮合併。例如本題,我們所需要從葉子節點把維護的資料不斷遞推上來,所以就需要線段樹合併。

肯定是動態開節點

這樣的題如果寫平衡樹啟發式合併的話,就要帶兩個log,而線段樹合併一個log就可以,雖然每一次合併複雜度不確定,我們考慮,對於每一次操作,都是把兩個線段樹合併,

而整個葉子節點共n顆線段樹,每一顆只有一個元素,顯然無論如何合併,n-1次之後我們就能將他們完全合。整個過程的複雜度不會比空樹中插入n個整數來的大。

線段樹合併關鍵是結構相同,所以我們可以把這個思想套在trie上

至於空間是n log n的!證明?

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<stack>
#include<utility>
#define fi first
#define se second
#define MK(a,b) make_pair((a),(b))
#define pii pair<int,ll>
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<int> 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;
}