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;
}