线段树的合并

研究了两天终于明白了一些
先要明白为什么要合并线段树,其实本质上就是我有两个数据结构(两个线段树)交汇,然后将其合并,数据也合并,对于线段树往往是权值线段树(类似于树状数组统计逆序对一样),但是建的线段树和之前有些区别,假设有 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值