题意:
晚上有n个亮着的灯泡,标号从1到n。
现在存在2种操作,如下:
操作1,关掉标号 [l,r] 区间的灯 操作2,打开标号 [l,r] 区间的灯
下面有q次询问,每次询问执行其中一种操作,询问格式,l,r,k,k为执行操作种类。对于每次询问回答当前开着的灯的数量。
re代码:
感觉没什么问题,但是因为空间问题炸了,不然应该可以过
#include<bits/stdc++.h>
using namespace std;
#define inf 1<<25
typedef long long ll;
const ll maxn=1e6+5;
struct vain
{
ll l,r,an,bn,lazy,sum;
} tr[maxn<<2];
void pushup(ll k)
{
tr[k].sum=tr[k<<1].an+tr[k<<1|1].an;
tr[k].an=tr[k<<1].an+tr[k<<1|1].an;
tr[k].bn=tr[k<<1].bn+tr[k<<1|1].bn;
}
void pushdown(ll k)
{
if(tr[k].lazy&1)
{
tr[k<<1].sum-=tr[k<<1].an;
tr[k<<1].bn+=tr[k<<1].an;
tr[k<<1].an=0;
tr[k<<1|1].sum-=tr[k<<1|1].an;
tr[k<<1|1].bn+=tr[k<<1|1].an;
tr[k<<1|1].an=0;
tr[k<<1].lazy=tr[k<<1|1].lazy=tr[k].lazy;
tr[k].lazy=0;
}
else if(tr[k].lazy==2)
{
tr[k<<1].sum+=tr[k<<1].bn;
tr[k<<1].an+=tr[k<<1].bn;
tr[k<<1].bn=0;
tr[k<<1|1].sum+=tr[k<<1|1].bn;
tr[k<<1|1].an+=tr[k<<1|1].bn;
tr[k<<1|1].bn=0;
tr[k<<1].lazy=tr[k<<1|1].lazy=tr[k].lazy;
tr[k].lazy=0;
}
}
void build(ll k,ll l,ll r)
{
tr[k].l=l,tr[k].r=r;
tr[k].lazy=0;
if(l==r)
{
tr[k].sum=1;
tr[k].an=1;
tr[k].bn=0;
return;
}
ll mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void modify(ll k,ll l,ll r,ll p)
{
if(tr[k].l>=l&&tr[k].r<=r)
{
if(p==1)
{
tr[k].sum-=tr[k].an;
tr[k].bn+=tr[k].an;
tr[k].an=0;
}
else
{
tr[k].sum+=tr[k].bn;
tr[k].an+=tr[k].bn;
tr[k].bn=0;
}
tr[k].lazy=p;
return ;
}
pushdown(k);
ll mid=tr[k].l+tr[k].r>>1;
if(mid>=r)
{
modify(k<<1,l,r,p);
}
else if(mid<l)
{
modify(k<<1|1,l,r,p);
}
else
{
modify(k<<1,l,mid,p);
modify(k<<1|1,mid+1,r,p);
}
pushup(k);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
ll n,q;
cin>>n>>q;
build(1,1,n);
while(q--)
{
ll l,r,p;
cin>>l>>r>>p;
modify(1,l,r,p);
cout<<tr[1].sum<<endl;
}
}
思路:
普通线段树会炸,需要用动态开点线段树来写(由于询问的点的数量并不是很多,但是范围比较大所以我们可以动态开点来节省空间),离散化感觉不太好写。我们用动态开点线段树,由于不断的更新区间,我们可以不断的得到已知区间中所有的关灯的数量,我们用 便是答案,另外说一下,这个就算加上
ios::sync_with_stdio(false)
也会T,建议用scanf
输入
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
struct vain
{
int l,r,sum,lazy;
} tr[maxn*50];
int cnt=1;
void pushdown(int l,int r,int k)
{
int mid=(l+r)>>1;
if (l!=r)
{
if (!tr[k].l) tr[k].l=++cnt;
if (!tr[k].r) tr[k].r=++cnt;
//下传判断儿子是否存在,不存在,便增加节点
if (tr[k].lazy==2)
tr[tr[k].l].sum=tr[tr[k].r].sum=0;
else
{
tr[tr[k].l].sum=mid-l+1;
tr[tr[k].r].sum=r-mid;
}
tr[tr[k].l].lazy=tr[k].lazy;
tr[tr[k].r].lazy=tr[k].lazy;
}
tr[k].lazy=0;
}
void inser(int l,int r,int &k,int L,int R,int p)
{
if (!k) k=++cnt;//遇到开过的点就不开,没开过开新的节点
if (l>=L && r<=R)
{
if (p==2) tr[k].sum=0;
else tr[k].sum=r-l+1;
tr[k].lazy=p;
return;
}
if (tr[k].lazy) pushdown(l,r,k);
int mid=(l+r)>>1;
if (mid>=L) inser(l,mid,tr[k].l,L,R,p);
if (mid<R) inser(mid+1,r,tr[k].r,L,R,p);
tr[k].sum=tr[tr[k].l].sum+tr[tr[k].r].sum;
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
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);
inser(1,n,k,l,r,p);
printf("%d\n",n-tr[1].sum);
}
}