大意:
晚上有n个亮着的灯泡,标号从1到n。
现在存在2种操作,如下:
- 操作1,关掉标号 [l,r] 区间的灯
- 操作2,打开标号 [l,r] 区间的灯
下面有q次询问,每次询问执行其中一种操作,询问格式,l,r,k。k为执行操作种类。对于每次询问回答当前开着的灯的数量。
第一行包含一个整数n,第二行一个整数q(1≤n≤10^9,1≤q≤3·10^5)
因为n范围过大,如果用静态开点需要离散化,但是离散化之后就无法根据离散化后的点得到区间长度 ,还需要再开数据记录,有点麻烦,所以这题我们用动态开点,不需要的点不开辟空间就好。
动态开点需要用l,r数组记录当前点的左右子节点,而不能再用rt<<1和rt<<1|1来表示了。
用数组记录左右子节点,如果左右子节点还没有出现过,给它赋值为cnt++(cnt用于给所有出现的节点编号,这样出现了多少点,就开辟了多少空间)
这题是区间更新,代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
struct vain
{
int l,r,sum,lazy;
} tree[maxn*50];
int cnt=1;
void pushdown(int l,int r,int k)
{
int mid=(l+r)>>1;
if (l!=r)
{
if (!tree[k].l)
tree[k].l=++cnt;
if (!tree[k].r)
tree[k].r=++cnt;
//下传判断儿子是否存在,不存在,便增加节点
tree[tree[k].l].sum=(mid-l+1)*tree[k].lazy;
tree[tree[k].r].sum=(r-mid)*tree[k].lazy;
tree[tree[k].l].lazy=tree[k].lazy;
tree[tree[k].r].lazy=tree[k].lazy;
}
tree[k].lazy=-1;
}
void update(int l,int r,int &k,int L,int R,int p)
{
if (!k)
{
k=++cnt;//遇到开过的点就不开,没开过开新的节点
tree[k].lazy=-1;
}
if (l>=L && r<=R)
{
tree[k].sum=p*(r-l+1);
tree[k].lazy=p;
return;
}
if (tree[k].lazy!=-1)
pushdown(l,r,k);
int mid=(l+r)>>1;
if (mid>=L)
update(l,mid,tree[k].l,L,R,p);
if (mid<R)
update(mid+1,r,tree[k].r,L,R,p);
tree[k].sum=tree[tree[k].l].sum+tree[tree[k].r].sum;
}
int main()
{
int n,q;
scanf("%d %d",&n,&q);
int k=1;
for (int i=1; i<=q; i++)
{
int l,r,p;
scanf("%d %d %d",&l,&r,&p);
p=2-p; //打开为0,关闭为1,因为初始化值为0,而所有的灯初始状态都是打开的
update(1,n,k,l,r,p);
printf("%d\n",n-tree[1].sum);
}
}
单点更新:
不需要给左右子节点赋值,因为不需要下传
#include<bits/stdc++.h>
#define LOG 20
using namespace std;
const int maxn=100010;
struct node
{
int l,r,sum;
}k[maxn*20];
int rt,ncnt,lc[maxn*LOG],rc[maxn*LOG],sum[maxn*LOG];
inline void pushup(int x)
{
k[x].sum=k[k[x].l].sum+k[k[x].r].sum;//更新
}
inline void update(int &x,int l,int r,int m,int val)
{
if(!x)
x=++ncnt;//开点
if(l==r)
{
k[x].sum+=val;
return;
}
int mid=(l+r)>>1;
if(m<=mid)
update(k[x].l,l,mid,m,val);
else
update(k[x].r,mid+1,r,m,val);
pushup(x);
}
int ask(int x,int l,int r,int L,int R)
{
if(!x)
return 0;//没这个点,直接返回0
if(L<=l && R>=r)
return k[x].sum;
int val=0;
int mid=(l+r)>>1;
if(L<=mid)
val+=ask(k[x].l,l,mid,L,R);
if(R>mid)
val+=ask(k[x].r,mid+1,r,L,R);//递归计算
return val;
}
int main()
{
int n;
cin>>n;
for(int i=1; i<=n; i++)
{
int num;
cin>>num;
update(rt,1,n,i,num);
}
int q;
cin>>q;
while(q--)
{
int l,r;
cin>>l>>r;
cout<<ask(rt,1,n,l,r)<<endl;
}
}