-
CF915E Physical Education Lessons
- CodeForces 不允许您多次提交相同的一份代码。如您多次提交相同代码,则有可能会被返回此错误。如确实需要多次提交一份代码,请自行添加一些注释 ~
题目大意
给出一个数列,初始每个元素都为1,每次操作把区间 ( l , r ) (l,r) (l,r)的所有元素变为1或者0,求每次操作后 ( 1 , n ) (1,n) (1,n)区间所有元素的和.
题目解析
涉及区间修改、区间求和的问题,自然想到用线段树来做.
但是观察数据范围,发现最多有 n ( n < 1 e 9 ) n(n<1e9) n(n<1e9)个元素,显然不能建完整的线段树,所幸询问 q q q的数据范围还算正常,所以我们考虑动态开点,点1表示区间 ( 1 , n ) (1,n) (1,n)的所有元素的和,用 t r e e . l tree.l tree.l和 t r e e . r tree.r tree.r维护左右子树,而且只有在需要子树时才给他们开点.
这样每次我们询问,就会向下开点,时间复杂度为 O ( q   l o g   q ) O(q\:log \:q) O(qlogq),空间复杂度为 O ( 2 ∗ q   l o g   q ) O(2*q\:log\:q) O(2∗qlogq),满足题目条件.
程序实现
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define maxn 15000010//1.5e7=3e5*25*2,log 3e5=25
using namespace std;
struct Tree{
int val,l,r;
}tree[maxn];
int n,q,tag[maxn],cnt;
void push_down(int p,int l,int r){
if(tag[p]==-1)return ;//同hotel,设了一个状态来表示不用下传懒标记
if(!tree[p].l ) tree[p].l =++cnt;
if(!tree[p].r ) tree[p].r =++cnt;
tree[tree[p].l ].val =tag[p]*(mid-l+1);
tree[tree[p].r ].val =tag[p]*(r-mid);//区间一起修改
tag[tree[p].l ]=tag[p];
tag[tree[p].r ]=tag[p];
tag[p]=-1;
return ;
}
int update(int p,int l,int r,int rl,int rr,int val){
if(p==0)p=++cnt;
if(rl<=l&&r<=rr){
tag[p]=val;
tree[p].val =val*(r-l+1);
return p;//修改节点值
}
push_down(p,l,r);//下放懒标记
if(rl<=mid) tree[p].l =update(tree[p].l ,l,mid,rl,rr,val);
if(mid<rr) tree[p].r =update(tree[p].r ,mid+1,r,rl,rr,val);
tree[p].val =tree[tree[p].l ].val +tree[tree[p].r ].val ;//更新父节点信息
return p;
}
int main(){
memset(tag,-1,sizeof tag);//-1表示不用下传懒标记的状态
scanf("%d%d",&n,&q);
int root=update(0,1,n,1,n,1);
for(int i=1,l,r,k;i<=q;i++){
scanf("%d%d%d",&l,&r,&k);
if(k==1){
int x=update(1,1,n,l,r,0);//更新对应区间
printf("%d\n",tree[1].val );//1号节点表示区间(1,n)
}
else {
int x=update(1,1,n,l,r,1);
printf("%d\n",tree[1].val );
}
}
return 0;
}
题后总结
这道题卡空间卡的很死,多开了一个1.5e7的数组都过不了.
既然是线段树,就一定要想到懒标记这种必要的优化.
要记得动态开点线段树,不过就是改变了一种维护父子关系的方法而已.