P2846 [USACO08NOV] Light Switching G线段树


原题:
农夫约翰试图让奶牛玩智力玩具来保持它们的敏锐。谷仓里的灯是较大的玩具之一。N(2≤N≤105) 个牛栏编号为 1…N,每个牛栏上面都有一盏灯。起初所有的灯都关着。

共有 M 次操作,分为两种。

指定一个区间 [Si​,Ei​],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
指定一个区间 [Si​,Ei​],要求你输出这个区间内有多少盏灯是打开的。

一、整体思路

观察题目,涉及区间修改和区间查询,询问数量最多可以达到1e6,暴力查询会T,我们使用线段树,把每个询问的时间优化到logn,整个算法的时间复杂度就是O(mlogn),2s的时限是可以接受的。

二、实现细节

1.操作简化

我们可以把开灯的状态定义为1,反之为0.对于一个区间,偶数次修改相当于不变,我们可以使用异或(XOR)来快捷完成这个转换,代码像这样

 l.tag ^= 1;  r.tag ^= 1;

2.其他细节

这题线段树每个节点的初值都是0,可以不建树。
后面就是常规的区修和区查


AC代码

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const int N = 100005;
struct note
{
    int l,r,sum,tag;
}tree[N*4];
int ls(int p);
int rs(int p);
void buildTree(int p,int l,int r);
void pushup(int p);
void pushdown(int p);
void update(int p,int l,int r);
int query(int p,int l,int r);
int main()
{
    IOS;   int n,q;  cin >> n >> q;   buildTree(1,1,n);
    for (int i = 1,op,l,r; i <= q; i++)
    {
        cin >> op >> l >> r;
        if (l > r) swap(l,r);
        if (op)   cout << query(1,l,r) << "\n";
        else  update(1,l,r);
    }
    return 0;
}
int ls(int p)
{
    return p << 1;
}
int rs(int p)
{
    return p << 1 | 1;
}
void buildTree(int p,int l,int r)
{
    tree[p] = {l,r,0,0};
    if (l == r)  return;
    int mid = l + r >> 1;
    buildTree(ls(p),l,mid);
    buildTree(rs(p),mid+1,r);
    pushup(p);
}
void pushup(int p)
{
    tree[p].sum = tree[ls(p)].sum + tree[rs(p)].sum;
}
void pushdown(int p)
{
    if (tree[p].tag)
    {
        tree[ls(p)].sum = (tree[ls(p)].r - tree[ls(p)].l + 1) - tree[ls(p)].sum;
        tree[rs(p)].sum = (tree[rs(p)].r - tree[rs(p)].l + 1) - tree[rs(p)].sum;
        tree[ls(p)].tag ^= 1;   tree[rs(p)].tag ^= 1;
        tree[p].tag ^= 1;
    }
}
void update(int p,int l,int r)
{
    if (l <= tree[p].l && tree[p].r <= r)
    {
        tree[p].tag ^= 1;
        tree[p].sum = (tree[p].r - tree[p].l + 1) - tree[p].sum;
        return;
    }
    pushdown(p);
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid)  update(ls(p),l,r);
    if (r > mid)   update(rs(p),l,r);
    pushup(p);
}
int query(int p,int l,int r)
{
    if (l <= tree[p].l && tree[p].r <= r)  return tree[p].sum;
    pushdown(p);
    int ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid)  ans += query(ls(p),l,r);
    if (r > mid)  ans += query(rs(p),l,r);
    return ans;
}

测评结果

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值