2020NYIST个人积分赛第一场 C(动态开点线段树)

题意:

晚上有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;
    }

}

思路:

普通线段树会炸,需要用动态开点线段树来写(由于询问的点的数量并不是很多,但是范围比较大所以我们可以动态开点来节省空间),离散化感觉不太好写。我们用动态开点线段树,由于不断的更新区间,我们可以不断的得到已知区间中所有的关灯的数量,我们用 n t r [ 1 ] . s u m n-tr[1].sum 便是答案,另外说一下,这个就算加上 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);
    }
}
发布了254 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yangzijiangac/article/details/105256993