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

京公网安备 11010502036488号