话说像这样的,维护的东西需要数据结构且需要合并的问题,就可以考虑合并。例如本题,我们所需要从叶子节点把维护的数据不断递推上来,所以就需要线段树合并。
肯定是动态开节点
这样的题如果写平衡树启发式合并的话,就要带两个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;
}