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

【线段树 容斥原理】P3801 红色的幻想乡|普及+

本文涉及知识点

C++线段树 01线段树
容斥原理

P3801 红色的幻想乡

题目背景

蕾米莉亚的红雾异变失败后,很不甘心。

题目描述

经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放。

我们将幻想乡看做是一个 n × m n \times m n×m的方格地区,一开始没有任何一个地区被红雾遮盖。蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区。如果两阵红雾碰撞,则会因为密度过大而沉降消失。灵梦察觉到了这次异变,决定去解决它。但在解决之前,灵梦想要了解一片范围红雾的密度。可以简述为两种操作:

1 x y 蕾米莉亚站在坐标 ( x , y ) (x,y) (x,y) 的位置向四个方向释放无限长的红雾。

2 x1 y1 x2 y2 询问左上点为 ( x 1 , y 1 ) (x1,y1) (x1,y1),右下点为 ( x 2 , y 2 ) (x2,y2) (x2,y2) 的矩形范围内,被红雾遮盖的地区的数量。

输入格式

第一行三个整数 n , m , q n,m,q n,m,q,表示幻想乡大小为 n × m n \times m n×m,有 q q q 个询问。

接下来 q q q 行,每行 3 3 3 个或 5 5 5 个整数,用空格隔开,含义见题目描述。

输出格式

对于每一个操作 2 2 2,输出一行一个整数,表示对应询问的答案。

输入输出样例 #1

输入 #1

4 4 3
1 2 2
1 4 4
2 1 1 4 4

输出 #1

8

说明/提示

样例输入输出 1 解释

o表示没有红雾,x表示有红雾,两次释放红雾后幻想乡地图如下:

oxox
xoxo
oxox
xoxo

数据规模与约定
  • 对于 20 % 20\% 20% 的数据, 1 ≤ n , m , q ≤ 200 1 \le n,m,q \le 200 1n,m,q200
  • 对于 40 % 40\% 40% 的数据, 1 ≤ n , m , q ≤ 1 0 3 1 \le n,m,q \le 10^3 1n,m,q103
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n , m , q ≤ 1 0 5 1 \le n,m,q \le 10^5 1n,m,q105 1 ≤ x 1 , x 2 , x ≤ n 1 \le x_1,x_2,x \le n 1x1,x2,xn x 1 ≤ x 2 x_1 \le x_2 x1x2 1 ≤ y 1 , y 2 , y ≤ m 1 \le y_1,y_2,y \le m 1y1,y2,ym y 1 ≤ y 2 y_1 \le y_2 y1y2

线段树

操作一可以拆分成两个子操作:
一,水平红雾。
二,竖直红雾。
性质一:同一个单格释放偶数次红雾(不论水平、竖直) ⟺ \iff 此行不释放红雾。同理,释放奇数次红雾,等同于释放一次红雾。
性质二:R行C列的矩形,r行有水平红雾,c列有竖直红雾。则:
覆盖2层红雾:x1=r × \times ×c
覆盖0层红雾:x2=(R-c) × \times ×(C-c)
余下的就是一层红雾,本查询的答案是:R × \times ×C-x1-x2。
两个01求和线段树,分别记录覆盖奇数次的行列。
线段树TSave:int 默认值0。
线段树TRecord:int 默认0。
查询、更新父节点:求和。
更新节点:如果update是1(iSaveRight-iSaveLeft+1)-save
更新缓存:old = (old+iNew)%2
注意
本题n是列,m是行。

代码

核心代码

#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(TSave& ans, 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_tDefault(tDefault), 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);}TSave Query(int leftIndex, int rightIndex) {return Query(leftIndex, rightIndex, m_tDefault);}TSave Query(int leftIndex, int rightIndex, const TSave& tDefault) {TSave ans = tDefault;Query(ans, 1, 0, m_iEleSize - 1, leftIndex, rightIndex);return ans;}//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(TSave& ans, int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) {if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) {this->OnQuery(ans, 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(ans, iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight);}if (mid + 1 <= iQueryRight) {Query(ans, 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;TSave m_tDefault;const int m_iEleSize;
};typedef int TSave;
typedef int  TRecord;
class  CMyLineTree : public  CVectorRangeUpdateLineTree<TSave, TRecord>
{
public:using CVectorRangeUpdateLineTree::CVectorRangeUpdateLineTree;
protected:virtual void OnQuery(TSave& ans, const TSave& save, const int& iSaveLeft, const int& iSaveRight) {ans += save;}virtual void OnUpdate(TSave& save, const int& iSaveLeft, const int& iSaveRight, const TRecord& update) override{if (1 == update) {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;}
};
class Solution {
public:vector<long long> Ans(const int C, const int R, vector<tuple<int, int, int, int>>& que) {CMyLineTree lineTreeR(R + 1, 0, 0), lineTreeC(C + 1, 0, 0);vector<long long> ans;for (const auto& [x, y, x2, y2] : que) {if (0 == x2) {lineTreeR.Update(y, y, 1);lineTreeC.Update(x, x, 1);}else{const long long RC = y2 - y + 1;const long long CC = x2 - x + 1;const long long r = lineTreeR.Query(y, y2);const long long c = lineTreeC.Query(x, x2);ans.emplace_back(RC * CC - r * c - (RC - r) * (CC - c));}}return ans;}
};int main() {	
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG		int R, C,q;cin >> R >> C>> q;vector<tuple<int, int, int, int>> que(q);int kind;for (int i = 0; i < q; i++) {cin >> kind >> get<0>(que[i]) >>  get<1>(que[i]);if (2 == kind) {cin >> get<2>(que[i]) >> get<3>(que[i]);assert(get<0>(que[i]) <= get<2>(que[i]));assert(get<1>(que[i]) <= get<3>(que[i]));}}
#ifdef _DEBUG		/*printf("T=%d,", T);*//*Out(a, "a=");*/Out(que, "que=");
#endif // DEBUG	auto res = Solution().Ans(R,C,que);for (const auto& i : res){cout << i << endl;}return 0;
}

单元测试

	vector<int> a;vector<tuple<int, int, int, int>> que;TEST_METHOD(TestMethod11){que = { {2,2,0,0},{4,4,0,0},{1,1,4,4} };auto res = Solution().Ans(4,4, que);AssertEx({ 8LL }, 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++**实现。

相关文章:

  • 大学生如何学好人工智能
  • TI MSP430搭配 SD NAND(贴片式T卡):长续航心电监测的可靠保障
  • 大数据去重
  • 机器人操作中的生成式 AI:综述(下)
  • 并发与并行的本质区别与实战应用解析
  • Python设计模式:MVC模式
  • 如何检测Python项目哪些依赖库没有使用
  • Spring—循环依赖与三级缓存
  • 双模对讲机与单模对讲机:技术革新与通信效能的深度解析
  • STM32F103C8T6 GPIO 通讯原理与物理层解析
  • 第二章 安全认证
  • centos7使用certbot完成nginx ssl证书续期
  • Linux编译器-gcc/g++使用
  • 【hexo主题自定义】
  • OpenFeign和Gateway
  • Android Build Variants(构建变体)详解
  • NEWport太阳光模拟器与AVANTES光谱仪与太阳能模拟器光谱匹配度检测应用
  • 【UVM项目实战】异步fifo—uvm项目结构以及uvm环境搭建
  • Unity性能优化实战:用Profiler揪出卡顿元凶 (CPU/GPU/内存/GC全面解析) (Day 37)
  • java延迟map, 自定义延迟map, 过期清理map,map能力扩展。如何设置map数据过期,改造map适配数据过期
  • 远程控制、窃密、挖矿!我国境内捕获“银狐”木马病毒变种
  • 公安部知识产权犯罪侦查局:侦破盗录传播春节档院线电影刑案25起
  • 2025一季度,上海有两把刷子
  • 苗旋已任民航局空管局局长、党委副书记
  • 限制再放宽!新版市场准入负面清单缩减到106项
  • 宁德时代校友红利!副董事长给母校复旦豪捐10亿,曾毓群给交大捐近14亿