研究了两天终于明白了一些
先要明白为什么要合并线段树,其实本质上就是我有两个数据结构(两个线段树)交汇,然后将其合并,数据也合并,对于线段树往往是权值线段树(类似于树状数组统计逆序对一样),但是建的线段树和之前有些区别,假设有
105
10
5
个点每个点都建一个线段树,那内存直接爆炸了,所以是另一种隐式的建树,就是说,如果每个线段树的点没有涉及到,那我就不创建就好了嘛,所以我们需要动态开辟内存或者使用数组指针(其实本质上我们原先正常写的线段树其实就是数组指针嘛,只不过利用了完全二叉树的性质找到左右子节点而已)
线段树节点:
struct node
{
int val;
node* l;
node* r;
}
为某个单个点建线段树(逻辑上):实际上建了一个链表,节点个数是 log2(n) l o g 2 ( n )
void build(int l,int r,int x)
{
node* p=new node;
p->l=p->r=NULL;
p->val=1;
if(l==r)return p;
int mid=(l+r)>>1;
if(x<=mid)
p->l=build(l,mid,x);
else
p->r=build(mid+1,r,x);
return p;
}
合并:也非常好写的
node* _merge(int l,int r,node* a,node* b)
{
if(a==NULL)return b;
if(b==NULL)return a;
node* p=new node();
p->l=p->r=NULL;
p->val=a->val+b->val;
if(l==r)return p;
int mid=(l+r)>>1;
p->l=_merge(l,mid,a->l,b->l);
p->r=_merge(mid+!,a->r,b->r);
delete(a);delete(b);
return p;
}
例题:BZOJ2212: [Poi2011]Tree Rotations
题目描述:
现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有n个叶子节点,满足这些权值为1..n的一个排列)。可以任意交换每个非叶子节点的左右孩子。
要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。
input:
第一行n
下面每行,一个数x
如果x==0,表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息,
如果x!=0,表示这个节点是叶子节点,权值为x
1<=n<=200000
output:
一行,最少逆序对个数
sample input
3
0
0
3
1
2
output:
1
给每个叶子节点建线段树,然后从下到上即dfs,回溯的适合合并并统计答案。
代码:但是动态指针这种写法超时了,换数组试试。
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxx 200005
using namespace std;
struct node
{
int countt;
node* l;
node* r;
};
long long ans,res1,res2;
int n;
node* build(int l,int r,int x)
{
node* p=new node;
p->countt=1;
p->l=p->r=NULL;
if(l==r)return p;
int mid=(l+r)>>1;
if(x<=mid) p->l=build(l,mid,x);
else p->r=build(mid+1,r,x);
return p;
}
node* _merge(int l,int r,node* u,node* v)
{
if(u==NULL) return v;
if(v==NULL) return u;
node* p=new node;
p->countt=u->countt+v->countt;
if(l==r) return p;
int mid=(l+r)>>1;
if(u->r!=NULL&&v->l!=NULL)
res1+=(long long)(u->r->countt)*(v->l->countt);
if(u->l!=NULL&&v->r!=NULL)
res2+=(long long)(u->l->countt)*(v->r->countt);
p->l=_merge(l,mid,u->l,v->l);
p->r=_merge(mid+1,r,u->r,v->r);
delete(u);delete(v);
return p;
}
int tmp;
node* dfs()
{
scanf("%d",&tmp);
if(tmp)return build(1,n,tmp);
node* p=_merge(1,n,dfs(),dfs());
ans+=min(res1,res2);
res1=res2=0;
return p;
}
int main()
{
cin>>n;
node* root=dfs();
cout<<ans<<endl;
return 0;
}
ac代码:数组过了,看样子的确数组会快一点,而且写起来也简单和简洁
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxx 10000005
using namespace std;
int n;
int ls[maxx],rs[maxx],val[maxx];
int cnt=0;
long long ans,res1,res2;
int build(int l,int r,int x)
{
val[++cnt]=1;
if(l==r) return cnt;
int node=cnt;
int mid=(l+r)>>1;
if(x<=mid)
ls[node]=build(l,mid,x);
else
rs[node]=build(mid+1,r,x);
return node;
}
int _merge(int l,int r,int a,int b)
{
if(!a||!b)return a+b;
val[++cnt]=val[a]+val[b];
int node=cnt;
if(l==r) return node;
int mid=(l+r)>>1;
res1+=(long long)val[rs[a]]*val[ls[b]];
res2+=(long long)val[ls[a]]*val[rs[b]];
ls[node]=_merge(l,mid,ls[a],ls[b]);
rs[node]=_merge(mid+1,r,rs[a],rs[b]);
return node;
}
int temp;
int dfs()
{
scanf("%d",&temp);
if(temp) return build(1,n,temp);
int node=_merge(1,n,dfs(),dfs());
ans+=min(res1,res2);
res1=res2=0;
return node;
}
int main()
{
cin>>n;
dfs();
cout<<ans<<endl;
return 0;
}