当前位置: 首页 > news >正文

【线段树】P2846 [USACO08NOV] Light Switching G|LG4|普及+

本文涉及知识点

C++线段树

[USACO08NOV] Light Switching G

题目描述

Farmer John tries to keep the cows sharp by letting them play with intellectual toys. One of the larger toys is the lights in the barn. Each of the N ( 2 ≤ N ≤ 10 5 ) N (2 \le N \le 10^5) N(2N105) cow stalls conveniently numbered 1 … N 1\ldots N 1N has a colorful light above it.

At the beginning of the evening, all the lights are off. The cows control the lights with a set of N pushbutton switches that toggle the lights; pushing switch i changes the state of light i from off to on or from on to off.

The cows read and execute a list of M ( 1 ≤ M ≤ 10 5 ) M(1\le M \le 10^5) M(1M105) operations expressed as one of two integers ( 0 ≤ 0 \le 0 operation ≤ 1 \le 1 1 ).

The first kind of operation (denoted by a 0 0 0 command) includes two subsequent integers S i S_i Si and E i ( 1 ≤ S i ≤ E i ≤ N ) E_i (1 \le S_i \le E_i \le N) Ei(1SiEiN) that indicate a starting switch and ending switch. They execute the operation by pushing each pushbutton from S i S_i Si through E i E_i Ei inclusive exactly once.

The second kind of operation (denoted by a 1 1 1 command) asks the cows to count how many lights are on in the range given by two integers S i S_i Si and E i ( 1 ≤ S i ≤ E i ≤ N ) E_i (1 \le S_i \le E_i \le N) Ei(1SiEiN) which specify the inclusive range in which the cows should count the number of lights that are on.

Help FJ ensure the cows are getting the correct answer by processing the list and producing the proper counts.

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

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

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

输入格式

1 1 1 行: 用空格隔开的两个整数 N N N M M M N N N 是灯数。

2 … M + 1 2\ldots M+1 2M+1 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号, S i S_i Si E i E_i Ei

若指令号为 0 0 0,则表示改变 [ S i , E i ] [S_i,E_i] [Si,Ei] 区间内的灯的状态(把开着的灯关上,关着的灯打开)。

若指令号为 1 1 1,则表示输出 [ S i , E i ] [S_i,E_i] [Si,Ei] 这个区间内有多少盏灯是打开的。

输出格式

样例 #1

样例输入 #1

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

样例输出 #1

1
2

提示

数据点编号 N N N M M M
1 ∼ 2 1\sim 2 12 ≤ 100 \le 100 100 ≤ 100 \le 100 100
3 ∼ 4 3\sim 4 34 ≤ 1000 \le 1000 1000 ≤ 1000 \le 1000 1000
5 ∼ 6 5\sim 6 56 ≤ 10000 \le 10000 10000 ≤ 10000 \le 10000 10000
7 ∼ 8 7\sim 8 78 ≤ 10 5 \le 10^5 105 ≤ 100 \le 100 100
9 ∼ 10 9\sim 10 910 ≤ 100 \le 100 100 ≤ 10 5 \le 10^5 105
11 ∼ 12 11\sim 12 1112 ≤ 1000 \le 1000 1000 ≤ 10 5 \le 10^5 105
13 ∼ 14 13\sim 14 1314 ≤ 10 5 \le 10^5 105 ≤ 1000 \le 1000 1000
15 ∼ 16 15\sim 16 1516 ≤ 10000 \le 10000 10000 ≤ 10000 \le 10000 10000
17 ∼ 18 17\sim 18 1718 ≤ 10 \le 10 10 ≤ 10 5 \le 10^5 105
19 ∼ 20 19\sim 20 1920 ≤ 2000 \le 2000 2000 ≤ 10 6 \le 10^6 106

求和线段树

相加后对2取模。
保存类型int: 当前节点有多少盏灯开着。
设置缓存类型int:开灯、关灯次数。
叶子节点和非叶子节点的更新函数相同:

virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) override
{
if (0 == update % 2) { return; }
save = (iSaveRight - iSaveLeft + 1) - save;
}

代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) ;return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;scanf("%d", &n);vector<T> ret(n);for(int i=0;i < n ;i++) {cin >> ret[i];}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 12 * 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;}inline void write(char ch){*m_p++ = ch;}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);}
private:char  puffer[N], * m_p;
};template<int N = 12 * 1'000'000>
class CInBuff
{
public:inline CInBuff() {fread(buffer, 1, N, stdin);}inline int Read() {int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);return f ? -x : x;}
private:char buffer[N], * S = buffer;
};template<class TSave, class TRecord >
class CRangUpdateLineTree
{
protected:virtual void OnQuery(const TSave& save, const int& iSaveLeft, const int& iSaveRight) = 0;virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) = 0;virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, const int& iSaveLeft, const int& iSaveRight) = 0;virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) = 0;
};template<class TSave, class TRecord >
class CVectorRangeUpdateLineTree : public CRangUpdateLineTree<TSave, TRecord>
{
public:CVectorRangeUpdateLineTree(int iEleSize, TSave tDefault, TRecord tRecordNull) :m_iEleSize(iEleSize), m_save(iEleSize * 4, tDefault), m_record(iEleSize * 4, tRecordNull) {m_recordNull = tRecordNull;}void Update(int iLeftIndex, int iRightIndex, TRecord value){Update(1, 0, m_iEleSize - 1, iLeftIndex, iRightIndex, value);}void Query(int leftIndex, int rightIndex) {Query(1, 0, m_iEleSize - 1, leftIndex, rightIndex);}//void Init() {//	Init(1, 0, m_iEleSize - 1);//}TSave QueryAll() {return m_save[1];}void swap(CVectorRangeUpdateLineTree<TSave, TRecord>& other) {m_save.swap(other.m_save);m_record.swap(other.m_record);std::swap(m_recordNull, other.m_recordNull);assert(m_iEleSize == other.m_iEleSize);}
protected://void Init(int iNodeNO, int iSaveLeft, int iSaveRight)//{//	if (iSaveLeft == iSaveRight) {//		this->OnInit(m_save[iNodeNO], iSaveLeft);//		return;//	}//	const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;//	Init(iNodeNO * 2, iSaveLeft, mid);//	Init(iNodeNO * 2 + 1, mid + 1, iSaveRight);//	this->OnUpdateParent(m_save[iNodeNO], m_save[iNodeNO * 2], m_save[iNodeNO * 2 + 1], iSaveLeft, iSaveRight);//}void Query(int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) {if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) {this->OnQuery(m_save[iNodeNO], iSaveLeft, iSaveRight);return;}if (iSaveLeft == iSaveRight) {//没有子节点return;}Fresh(iNodeNO, iSaveLeft, iSaveRight);const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;if (mid >= iQueryLeft) {Query(iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight);}if (mid + 1 <= iQueryRight) {Query(iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight);}}void Update(int iNode, int iSaveLeft, int iSaveRight, int iOpeLeft, int iOpeRight, TRecord value){if ((iOpeLeft <= iSaveLeft) && (iOpeRight >= iSaveRight)){this->OnUpdate(m_save[iNode], iSaveLeft, iSaveRight, value);this->OnUpdateRecord(m_record[iNode], value);return;}if (iSaveLeft == iSaveRight) {return;//没有子节点}Fresh(iNode, iSaveLeft, iSaveRight);const int iMid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;if (iMid >= iOpeLeft){Update(iNode * 2, iSaveLeft, iMid, iOpeLeft, iOpeRight, value);}if (iMid + 1 <= iOpeRight){Update(iNode * 2 + 1, iMid + 1, iSaveRight, iOpeLeft, iOpeRight, value);}// 如果有后代,至少两个后代this->OnUpdateParent(m_save[iNode], m_save[iNode * 2], m_save[iNode * 2 + 1], iSaveLeft, iSaveRight);}void Fresh(int iNode, int iDataLeft, int iDataRight){if (m_recordNull == m_record[iNode]){return;}const int iMid = iDataLeft + (iDataRight - iDataLeft) / 2;Update(iNode * 2, iDataLeft, iMid, iDataLeft, iMid, m_record[iNode]);Update(iNode * 2 + 1, iMid + 1, iDataRight, iMid + 1, iDataRight, m_record[iNode]);m_record[iNode] = m_recordNull;}vector<TSave> m_save;vector<TRecord> m_record;TRecord m_recordNull;const int m_iEleSize;
};typedef int TSave;
typedef int  TRecord;
class  CMyLineTree : public  CVectorRangeUpdateLineTree<TSave, TRecord>
{
public:int m_ans = 0;using CVectorRangeUpdateLineTree::CVectorRangeUpdateLineTree;
protected:virtual void OnQuery(const TSave& save, const int& iSaveLeft, const int& iSaveRight) {m_ans += save;}virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) override{if (0 == update % 2) { return; }save = (iSaveRight - iSaveLeft + 1) - save;}virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, const int& iSaveLeft, const int& iSaveRight)  override{par = left + r;}virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) override{old = (old + newRecord) % 2;}
};//P2846 [USACO08NOV] Light Switching G
class Solution {
public:vector<int> Ans(const int N, vector<tuple<int, int, int>>& que) {CMyLineTree lineTree(N + 1, 0, 0);vector<int> ans;for (const auto& [kind, L, R] : que) {if (0 == kind) {lineTree.Update(L, R, 1);}else {lineTree.m_ans = 0;lineTree.Query(L, R);ans.emplace_back(lineTree.m_ans);}}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG	int n, q;cin >> n >> q;auto que = Read<tuple<int, int, int>>(q);auto res = Solution().Ans(n,que);COutBuff<24*1000'000> ob;for (const auto& i : res) {ob.write(i);ob.write('\n');}ob.ToFile();
#ifdef _DEBUG	printf(",n=%d,", n);Out(que, "que=");	
#endif // DEBUG	return 0;
}

单元测试

int n;vector<tuple<int, int, int>> que;TEST_METHOD(TestMethod11){n = 4, que = { {0,1,2},{0,2,4},{1,2,3},{0,2,4},{1,1,4} };auto res = Solution().Ans(n, que);AssertEx({ 1,2 }, res);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

http://www.xdnf.cn/news/584731.html

相关文章:

  • 无人机集装箱箱号识别系统准确率如何?能达到多少?
  • 微服务架构中的 RabbitMQ:异步通信与服务解耦(一)
  • Linux探秘:驾驭开源,解锁高性能——基础指令(续集)
  • LeetCode 1340. 跳跃游戏 V(困难)
  • 【Harmony】【鸿蒙】List列表View如果刷新内部的自定义View
  • 力扣HOT100之二叉树: 236. 二叉树的最近公共祖先
  • vue3定于组件名字的几种方法
  • 杨校老师竞赛课之青科赛GOC5-6年级组模拟题
  • ISO 26262- 5 评估硬件度量值
  • 2025年中青杯赛题浅析-快速选题
  • 12kV 环保气体绝缘交流金属封闭开关设备现场交流耐压试验规范
  • Web前端开发(HTML、CSS快速入门)
  • 2024 年地理信息技术与应用技能大赛(选拔赛/初级)——实操试题
  • 部署Prometheus并通过Grafana展示界面
  • wx.getPrivacySetting接口needAuthorization一直返回false
  • vue element-plus 集成多语言
  • SQLynx:一款跨平台的企业级数据库管理工具
  • pdf图片导出(Visio和Origin)
  • 2025口语App实测Top5!练习口语app真实口碑
  • 可视化图解算法43:数组中的逆序对
  • 鸿蒙Flutter实战:24-混合开发详解-4-初始化Flutter
  • 鸿蒙Flutter实战:25-混合开发详解-5-跳转Flutter页面
  • [Flutter]Completer和compute
  • 计量单片机 RN8302:特性、使用与应用
  • 【人工智障生成日记1】从零开始训练本地小语言模型
  • 【无标题】西门子S7-1500PLC与西门子V90 PN伺服通讯控制项目程序项目程序,共有8轴,编码器信号直接输入到变频器内。
  • 系统架构设计(十八):ATAM
  • 《棒球百科》棒球运动规则·棒球1号位
  • 【竖排繁体识别】如何将竖排繁体图片文字识别转横排繁体,转横排简体导出文本文档,基于WPF和腾讯OCR的实现方案
  • 免费轻量便携截图 录屏 OCR 翻译四合一!提升办公效率