CodeVS1690 开关灯 解题报告【数据结构】【线段树】

题目描述 Description
YYX家门前的街上有N(2<=N<=100000)盏路灯,在晚上六点之前,这些路灯全是关着的,六点之后,会有M(2<=m<=100000)个人陆续按下开关,这些开关可以改变从第i盏灯到第j盏灯的状态,现在YYX想知道,从第x盏灯到第y盏灯中有多少是亮着的(1<=i,j,x,y<=N)
输入描述 Input Description
第 1 行: 用空格隔开的两个整数N和M
第 2..M+1 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号(0代表按下开关,1代表询问状态), x 和 y
输出描述 Output Description
第 1..询问总次数 行:对于每一次询问,输出询问的结果
样例输入 Sample Input
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
样例输出 Sample Output
1
2
数据范围及提示 Data Size & Hint
一共4盏灯,5个操作,下面是每次操作的状态(X代表关上的,O代表开着的):
XXXX -> OOXX -> OXOO -> 询问1~3 -> OOXX -> 询问1~4
解题报告
这道题的操作就区间取反+区间求和,用线段树实现。线段树的每一个点存储这段的和以及懒标记。其中懒标记的更新是nd->flag=!nd->flag(取反),而每一次求和应该是这段区间的长度减去原来的和(nd->sum=(rg-lf+1)-nd->sum)。
代码如下:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100000;
int n,m;
int a[N+5];
struct node
{
    int sum,flag;
    node *lson,*rson;
    void pushdown(int lf,int rg)
    {
        if(flag)
        {
            int mid=(lf+rg)>>1;
            lson->flag=!lson->flag; 
            lson->sum=(mid-lf+1)-lson->sum;
            rson->flag=!rson->flag;
            rson->sum=(rg-mid)-rson->sum;
            flag=0;
        }
    }
    void update()
    {
        sum=lson->sum+rson->sum;
    }
};
struct node dizhi[4*N+5],*tail=dizhi,*root;
node *build(int lf,int rg)
{
    node *nd=++tail;
    if(lf==rg)
    {
        nd->sum=0;
        nd->flag=0;
    }
    else
    {
        int mid=(lf+rg)>>1;
        nd->lson=build(lf,mid);
        nd->rson=build(mid+1,rg);
        nd->sum=nd->lson->sum+nd->rson->sum;
        nd->flag=0;
    }
    return nd;
}
void modify(node *nd,int lf,int rg,int L,int R)
{
    if(L<=lf&&rg<=R)
    {
        nd->sum=(rg-lf+1)-nd->sum;
        nd->flag=!nd->flag;
        return ;
    }
    nd->pushdown(lf,rg);
    int mid=(lf+rg)>>1;
    if(L<=mid)modify(nd->lson,lf,mid,L,R);
    if(R>mid)modify(nd->rson,mid+1,rg,L,R);
    nd->update();
}
int query(node *nd,int lf,int rg,int L,int R)
{
    if(L<=lf&&rg<=R)return nd->sum;
    int mid=(lf+rg)>>1;
    nd->pushdown(lf,rg);
    long long rt=0;
    if(L<=mid)rt+=query(nd->lson,lf,mid,L,R);
    if(R>mid)rt+=query(nd->rson,mid+1,rg,L,R);
    nd->update();
    return rt;
}
int main()
{
    scanf("%d%d",&n,&m);
    root=build(1,n);
    while(m--)
    {
        int opt,lf,rg;
        scanf("%d%d%d",&opt,&lf,&rg);
        if(!opt)modify(root,1,n,lf,rg);
        else printf("%d\n",query(root,1,n,lf,rg));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值