首页 > 其他分享 >洛谷 P3870 开关之线段树板子

洛谷 P3870 开关之线段树板子

时间:2024-08-09 20:05:06浏览次数:14  
标签:rt le 洛谷 int 线段 tree Downarrow P3870

洛谷P3870题解


传送锚点


摸鱼环节

[TJOI2009] 开关

题目描述

现有 \(n\) 盏灯排成一排,从左到右依次编号为:\(1\),\(2\),……,\(n\)。然后依次执行 \(m\) 项操作。

操作分为两种:

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

灯在初始时都是关着的。

输入格式

第一行有两个整数 \(n\) 和 \(m\),分别表示灯的数目和操作的数目。

接下来有 \(m\) 行,每行有三个整数,依次为:\(c\)、\(a\)、\(b\)。其中 \(c\) 表示操作的种类。

  • 当 \(c\) 的值为 \(0\) 时,表示是第一种操作。
  • 当 \(c\) 的值为 \(1\) 时,表示是第二种操作。

\(a\) 和 \(b\) 则分别表示了操作区间的左右边界。

输出格式

每当遇到第二种操作时,输出一行,包含一个整数,表示此时在查询的区间中打开的灯的数目。

样例 #1

样例输入 #1

4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4

样例输出 #1

1
2

提示

数据规模与约定

对于全部的测试点,保证 \(2\le n\le 10^5\),\(1\le m\le 10^5\),\(1\le a,b\le n\),\(c\in\{0,1\}\)。

又是一年春好处,在下不会线段树。

窝又写题解了。由于考虑到线段树的暴力骗分性实用性,于是我决定好好写棵线段树。(不得不说代码还是挺长的)


正片开始

1.建树。

当然不是所有题都需要建树的环节,但咱当模板来练就建一下。

  1. 需要递归的元素(根节点\(rt\),左端点\(l\),右端点\(r\))。
  2. 递归过程非常简单,在\(l==r\)时递归出口,其他的取中点对两边递归即可。

code:

void build(int rt,int l,int r)
{
    if(l==r){tree[rt]=0;return;}
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    up(rt);//向上更新
}

2.向上更新

对于每次的修改,我们都需要对线段树进行更新。于是:

code:

void up(int rt){ tree[rt]=tree[rt<<1]+tree[rt<<1|1];}

3.标记下传

懒标记是线段树很重要的一部分,也是线段树的核心部分,在标记下传的时候我们处理区间维护值的更新:

code:

void down(int rt,int l,int r)
{
    if(lazy[rt]==0) return;//无需更新
    lazy[rt<<1]^=1,lazy[rt<<1|1]^=1;//标记更新
    int mid=(l+r)>>1;
    tree[rt<<1]=(mid-l+1)-tree[rt<<1];
    tree[rt<<1|1]=(r-mid)-tree[rt<<1|1];//处理树上维护数值的更新
    lazy[rt]=0;//标记清空
}

4.更新处理

对于更新操作,我们需要找到一个区间\([l,r]\)刚好与区间\([L,R]\)重合,同样也是分为左右两个子区间进行递归处理。

code:

void update(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R){tree[rt]=(r-l+1)-tree[rt];lazy[rt]^=1;return;}//找到合法区间
    down(rt,l,r);//标记下传
    int mid=(l+r)>>1;
    if(L<=mid) update(rt<<1,l,mid,L,R);
    if(R>mid) update(rt<<1|1,mid+1,r,L,R);//递归处理两个区间
    up(rt);//更新树
}

5.询问操作

和更新操作类似,这里不做说明。

code:

int query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R){ return tree[rt];}//找到结果
    down(rt,l,r);//下传标记
    int mid=(l+r)>>1,a=0,b=0;
    if(L<=mid) a=query(rt<<1,l,mid,L,R);
    if(R>mid) b=query(rt<<1|1,mid+1,r,L,R);
    return a+b;
}

警钟敲爆:不要忘记标记下传和向上更新。


完整代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int tree[N<<2],lazy[N<<2],n,m,a[N];
void up(int rt){ tree[rt]=tree[rt<<1]+tree[rt<<1|1];}
void build(int rt,int l,int r)
{
    if(l==r){tree[rt]=0;return;}
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    up(rt);
}
void down(int rt,int l,int r)
{
    if(lazy[rt]==0) return;
    lazy[rt<<1]^=1,lazy[rt<<1|1]^=1;
    int mid=(l+r)>>1;
    tree[rt<<1]=(mid-l+1)-tree[rt<<1];
    tree[rt<<1|1]=(r-mid)-tree[rt<<1|1];
    lazy[rt]=0;
}
void update(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R){tree[rt]=(r-l+1)-tree[rt];lazy[rt]^=1;return;}
    down(rt,l,r);
    int mid=(l+r)>>1;
    if(L<=mid) update(rt<<1,l,mid,L,R);
    if(R>mid) update(rt<<1|1,mid+1,r,L,R);
    up(rt);
}
int query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R){ return tree[rt];}
    down(rt,l,r);
    int mid=(l+r)>>1,a=0,b=0;
    if(L<=mid) a=query(rt<<1,l,mid,L,R);
    if(R>mid) b=query(rt<<1|1,mid+1,r,L,R);
    return a+b;
}
int main()
{
    int n,m;
    cin>>n>>m;
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int c,a,b;cin>>c>>a>>b;
        if(c==0) update(1,1,n,a,b);
        else cout<<query(1,1,n,a,b)<<endl;
    }
    return 0;
}


完结收工!!!!!

个人主页

看完点赞,养成习惯

\(\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\)

标签:rt,le,洛谷,int,线段,tree,Downarrow,P3870
From: https://www.cnblogs.com/qc0817/p/18351424

相关文章

  • 8.9 线段树板子+三分补题+三维的bfs
    nowcoder训练区间线段树板子题,我们只需要把区间每一个点设置成1,然后修改的时候直接改点,然后查区间就行线段树维护最大字段和/01串最大连续1的个数模板题。把白色和黑色看成1/0两个数就行了。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlon......
  • 【线段树合并/树上差分】[P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
    【线段树合并/树上差分】P4556[Vani有约会]雨天的尾巴/【模板】线段树合并思路对\(x,y,lca(u,v),fa_{lca(u,v)}\)四个点进行树上差分,然后用线段树合并动态权值线段树。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;template<classNode>str......
  • 线段树合并模板
    template<classNode>structPersidentSegmentTree{#definelc(u)tr[u].l#definerc(u)tr[u].rconstintn;inttot=0;vector<Node>tr;vector<int>root;PersidentSegmentTree():n(0){}PersidentSegmentTree(in......
  • 题解 洛谷P1478 陶陶摘苹果(升级版)
    题目传送门https://www.luogu.com.cn/problem/P1478截图来自洛谷:这道题就是这道题的升级版而已,我们可以定义一个结构体分别存抓当前苹果的力气与高度。之后进行从第1个苹果到第n个苹果的循环,判断当前苹果高度是否够,力气是否够。最重要的是要排序,因为要摘得苹果最多,所以要先......
  • 洛谷P1194 买礼物之警钟敲爆
    洛谷P1194题解传送锚点摸鱼环节买礼物题目描述又到了一年一度的明明生日了,明明想要买\(B\)样东西,巧的是,这\(B\)样东西价格都是\(A\)元。但是,商店老板说最近有促销活动,也就是:如果你买了第\(I\)样东西,再买第\(J\)样,那么就可以只花\(K_{I,J}\)元,更巧的是,\(K_{I,......
  • 洛谷:P1308 [NOIP2011 普及组] 统计单词数
    题目描述一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数。现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给定的文章中出现的次数和第一次出现的位置。注意:匹配单词时,不区分大小写,但要......
  • 洛谷 P1019 [NOIP2000 提高组] 单词接龙 题解
    暴搜!!暴搜!!暴搜!!重要的事情说三遍#include<bits/stdc++.h>usingnamespacestd;constintN=25;intn,ans,use[N];strings,word[N];voiddfs(strings){intls=s.size();//s的lengthans=max(ans,ls);//求出最长的单词接龙for(inti=0;i<n......
  • 洛谷P2404 自然数的拆分问题——题解
    洛谷P2404题解传送锚点摸鱼环节自然数的拆分问题题目描述任何一个大于\(1\)的自然数\(n\),总可以拆分成若干个小于\(n\)的自然数之和。现在给你一个自然数\(n\),要求你求出\(n\)的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列......
  • 洛谷 P1125 [NOIP2008 提高组] 笨小猴
    [NOIP2008提高组]笨小猴题目描述笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!这种方法的具体描述如下:假设maxn......
  • 【题解】Solution Set - NOIP2024集训Day2 线段树
    【题解】SolutionSet-NOIP2024集训Day2线段树https://www.becoder.com.cn/contest/5431「CF1149C」TreeGenerator™结论:对于括号序列的一个子段,删去所有的匹配括号之后,剩下的不匹配的括号,按顺序构成树上的一条路径。Why?从括号序列的构造出发。每次(相当于开始遍历......