动态开点

1.前言

这里专门写一篇动态开点,因为上次学习点分树的时候很难受,这里专门写一篇动态开点,来记录一下...
所谓的动态开点,就是指你的线段树没必要建成满二叉树的形式,因为有些节点的访问根本用不到,类似lazy吧,但是lazy是时间上的节省,体现在后面查询时,而动态开点是在前面建树,对于空间的节省.


2.引例

CF915E Physical Education Lessons
这个题很容易想到就是线段树的区间修改和区间查询,但是它的一个难点就是n<=1e9.这可咋办?但是我们可以注意到q<=3e5.也就是说它访问的区间还是不超过6e5个点.如此我们是不是可以在更新的时候建树,顺带查询答案呢?显然是可以的,这就是动态开点.


3.代码

讲了这么多(少?)总觉得有点小小的抽象,那么下面就看看代码吧~:

#include <bits/stdc++.h>
using namespace std;
const int N=1e7+5e6;
int lazy[N],ls[N],rs[N],sum[N],root,cnt;//线段树节点u的左右两个端点.以及这个端点的权值.
void pushdown(int u,int l,int r)
{
    if(~lazy[u])
    {
        if(!ls[u])    ls[u]=++cnt;
        if(!rs[u])    rs[u]=++cnt;
        int mid=(l+r)>>1;
        sum[ls[u]]=(mid-l+1)*lazy[u];
        sum[rs[u]]=(r-mid)*lazy[u];
        lazy[ls[u]]=lazy[rs[u]]=lazy[u];
        lazy[u]=-1;
    }
}

void modify(int &u,int l,int r,int L,int R,int val)
{
    if(!u)    u=++cnt;
    if(L<=l&&r<=R)
    {
        sum[u]=val*(r-l+1);
        lazy[u]=val;
        return;
    }int mid=(l+r)>>1;
    pushdown(u,l,r);
    if(L<=mid)    modify(ls[u],l,mid,L,R,val);
    if(R>mid)    modify(rs[u],mid+1,r,L,R,val);
    sum[u]=sum[ls[u]]+sum[rs[u]];
}

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    //我们假设工作日为0,非工作日为1.这样比较方便.
    memset(lazy,-1,sizeof lazy);
    modify(root,1,n,1,n,0);
    while(q--)
    {
        int L,R,k;
        scanf("%d%d%d",&L,&R,&k);
        modify(root,1,n,L,R,2-k);//区间修改成2-k.
        printf("%d\n",n-sum[1]);//输出答案.
    }
    return 0;
}

lpt的小屋 文章被收录于专栏

我想要一份甜甜的爱情

全部评论
反手就是一个赞,我还在刷手机的时候,shy弟弟已经在学习了
1 回复 分享
发布于 2020-12-15 21:58

相关推荐

不愿透露姓名的神秘牛友
07-25 13:59
点赞 评论 收藏
分享
07-02 10:39
门头沟学院 Java
Steven267:说点真实的,都要秋招了,还没有实习,早干嘛去了,本来学历就差,现在知道急了,而且你这个简历完全可以写成一页,劣势太大了,建议转测试
点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务