本文涉及知识点
C++线段树
[HAOI2014] 贴海报
题目描述
Bytetown 城市要进行市长竞选,所有的选民可以畅所欲言地对竞选市长的候选人发表言论。为了统一管理,城市委员会为选民准备了一个张贴海报的 electoral 墙。
张贴规则如下:
-
electoral 墙是一个长度为 N N N 个单位的长方形,每个单位记为一个格子;
-
所有张贴的海报的高度必须与 electoral 墙的高度一致的;
-
每张海报以
A B
表示,即从第 A A A 个格子到第 B B B 个格子张贴海报; -
后贴的海报可以覆盖前面已贴的海报或部分海报。
现在请你判断,张贴完所有海报后,在 electoral 墙上还可以看见多少张海报。
输入格式
第一行,两个正整数 N , M N,M N,M,分别表示 electoral 墙的长度和海报个数。
接下来 M M M 行,每行两个正整数 A i , B i A_i,B_i Ai,Bi,表示每张海报张贴的位置。
输出格式
输出贴完所有海报后,在 electoral 墙上还可以看见的海报数。
样例 #1
样例输入 #1
100 5
1 4
2 6
8 10
3 4
7 10
样例输出 #1
4
提示
约束条件
10 ≤ N ≤ 10000000 , 1 ≤ M ≤ 1000 , 1 ≤ A i ≤ B i ≤ 10000000 10\le N \le 10000000,1\le M\le 1000,1\le A_i \le B_i \le 10000000 10≤N≤10000000,1≤M≤1000,1≤Ai≤Bi≤10000000
所有的数据都是正整数,数据之间有一个空格。
离散化+线段树
第i块海报,如果被第i+1到M块海报覆盖,则看不到。
由于M只有1000,故可以离散。
i = T to 1
线段树:
保存类型,int:被覆盖的点数
设置类型,int: 覆盖次数。
注意:
离散化的时候,不但要离散L,R,还要离散L-1,R+1。否则[1,6] [1,3] [5,6]会变成[1,4][1,2][3,4]。前者可以看到3个海报,后者只能看到2个。
代码
核心代码
#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;
};
template<class T = int>
class CDiscretize //离散化
{
public:
CDiscretize(vector<T> nums)
{
sort(nums.begin(), nums.end());
nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
m_nums = nums;
for (int i = 0; i < nums.size(); i++)
{
m_mValueToIndex[nums[i]] = i;
}
}
int operator[](const T value)const
{
auto it = m_mValueToIndex.find(value);
if (m_mValueToIndex.end() == it)
{
return -1;
}
return it->second;
}
int size()const
{
return m_mValueToIndex.size();
}
vector<T> m_nums;
protected:
unordered_map<T, int> m_mValueToIndex;
};
//P3740 [HAOI2014] 贴海报
typedef int TSave;
typedef int TRecord;
class CMyLineTree : public CVectorRangeUpdateLineTree<TSave, TRecord>
{
public:
TSave 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) { return; }
save = iSaveRight - iSaveLeft + 1;
}
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 += newRecord;
}
};
class Solution {
public:
int Ans(vector<pair< int, int>>& LR) {
vector<int> tmp;
for (const auto& [L, R] : LR) {
tmp.emplace_back(L - 1);
tmp.emplace_back(L);
tmp.emplace_back(R);
tmp.emplace_back(R+1);
}
CDiscretize dis(tmp);
const int N = dis.size();
CMyLineTree lineTree(N, 0, 0);
int ans = 0;
for (int i = LR.size() - 1; i >= 0; i--) {
const int left = dis[LR[i].first];
const int r = dis[LR[i].second];
lineTree.m_ans = 0;
lineTree.Query(left, r);
ans += lineTree.m_ans < (r - left + 1);
lineTree.Update(left, r, 1);
}
return ans;
}
};
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
int n,T;
cin >> T >> n ;
auto a = Read<pair<int,int>>(n);
auto res = Solution().Ans(a);
cout << res;
#ifdef _DEBUG
/*printf("T=%d,", T);*/
//Out(a, "a=");
//Out(edge, "edge=");
#endif // DEBUG
return 0;
}
单元测试
vector<pair< int, int>> a;
TEST_METHOD(TestMethod11)
{
a = { {1,4},{2,6},{8,10},{3,4},{7,10}};
auto res = Solution().Ans(a);
AssertEx(4, 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++**实现。