bzoj2212(線段樹合併第一道)
阿新 • • 發佈:2019-01-24
話說像這樣的,維護的東西需要資料結構且需要合併的問題,就可以考慮合併。例如本題,我們所需要從葉子節點把維護的資料不斷遞推上來,所以就需要線段樹合併。
肯定是動態開節點
這樣的題如果寫平衡樹啟發式合併的話,就要帶兩個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; }