【排序 离散化 二维前缀和】 P7149 [USACO20DEC] Rectangular Pasture S|普及+

本文涉及知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

离散化

P7149 [USACO20DEC] Rectangular Pasture S

题目描述

Farmer John 最大的牧草地可以被看作是一个由方格组成的巨大的二维方阵(想象一个巨大的棋盘)。现在,有 N N N 头奶牛正占据某些方格( 1 ≤ N ≤ 2500 1≤N≤2500 1≤N≤2500)。

Farmer John 想要建造一个可以包围一块矩形区域的栅栏;这个矩形必须四边与 x x x 轴和 y y y 轴平行,最少包含一个方格。请帮助他求出他可以包围在这样的区域内的不同的奶牛子集的数量。注意空集应当被计算为答案之一。

输入格式

输入的第一行包含一个整数 N N N。以下 N N N 行每行包含两个空格分隔的整数,表示一头奶牛所在方格的坐标 ( x , y ) (x,y) (x,y)。所有 x x x 坐标各不相同,所有 y y y 坐标各不相同。所有 x x x 与 y y y 的值均在 0 ... 10 9 0...10^9 0...109 范围内。

输出格式

输出 FJ 可以包围的奶牛的子集数量。可以证明这个数量可以用 64 位有符号整数型存储(例如 C/C++ 中的long long)。

输入输出样例 #1

输入 #1

复制代码
4
0 2
1 0
2 3
3 5

输出 #1

复制代码
13

说明/提示

共有 2 4 2^4 24 个子集。FJ 不能建造一个栅栏仅包围奶牛 1 1 1、 2 2 2、 4 4 4,或仅包围奶牛 2 2 2、 4 4 4,或仅包围奶牛 1 1 1、 4 4 4,所以答案为 2 4 − 3 = 16 − 3 = 13 2^4-3=16-3=13 24−3=16−3=13。

  • 测试点 2-3 满足 N ≤ 20 N≤20 N≤20。
  • 测试点 4-6 满足 N ≤ 100 N≤100 N≤100。
  • 测试点 7-12 满足 N ≤ 500 N≤500 N≤500。
  • 测试点 13-20 没有额外限制。

供题:Benjamin Qi

排序 分类讨论 树状数组 离散化

注意 :所有 x x x 坐标各不相同,所有 y y y 坐标各不相同。

按x排序,对y离散化,方便用树状数组结论[left+1,r-1]直接的y。

令最左、最右的奶牛纵坐标是left,r。

分类一:空矩形,只有一种情况。没有任何奶牛。

分类二:left=r,包围一头奶牛。共有N种情况。

分类三:left < r。令这两头奶牛的纵坐标为y1,y2,如果y1大于j2,交换之。x在[left+1,r-1],y小于等于y1的奶牛数量为z1,y大于等于y2奶牛的数量是z2。则ans +=z1*z2。
时间复杂度:O(nnlogn)

错误解法:离散化+二维前缀和

离散化后,二维前缀和。枚举左上角,右下角。如果最左(右上下)行(列)没有奶牛删除之。故矩形的左上、右下一定有奶牛。

左边缘一定有牛,上边缘一定有牛,但左上不一定有牛。

优化

x,y离散化后,用二维前缀和查询:mat[0...y1][left...r]和mat[y2...R-1]的奶牛数。
时间复杂度:O(nn)

代码

核心代码

cpp 复制代码
#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;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	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<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;
};
template<class T = int>
class CPreSum2 {
public:
	template<class _Pr>
	CPreSum2(int rowCnt, int colCount, _Pr pr) :m_iRowCnt(rowCnt), m_iColCnt(colCount) {
		m_vSum.assign(rowCnt + 1, vector<int>(colCount + 1));
		for (int r = 0; r < rowCnt; r++) {
			for (int c = 0; c < colCount; c++) {
				m_vSum[r + 1][c + 1] = m_vSum[r][c + 1] + m_vSum[r + 1][c] - m_vSum[r][c] + pr(r, c);
			}
		}
	}
	T Get(int left, int top, int right, int bottom)const {
		return m_vSum[bottom + 1][right + 1] - m_vSum[top][right + 1] - m_vSum[bottom + 1][left] + m_vSum[top][left];
	}
	T GetTopLeft(int bottom, int right) { return m_vSum[bottom + 1][right + 1]; }
	T GetBottomRight(int top, int left) { return Get(left, top, m_iColCnt - 1, m_iRowCnt - 1); }
	vector<vector<T>> m_vSum;
	const int m_iRowCnt, m_iColCnt;
};

class Solution {
public:
	long long Ans(vector<pair<int, int>>& pts) {
		const int N = pts.size();
		vector<int> xs, ys;
		for (const auto& [x, y] : pts) {
			xs.emplace_back(x); ys.emplace_back(y);
		}
		CDiscretize<int> disx(xs), disy(ys);
		const int R = disy.size(), C = disx.size();
		vector<vector<int>> mat(R, vector<int>(C));
		for (auto& [x, y] : pts) {
			x = disx[x]; y = disy[y];
			mat[y][x]++;
		}
		CPreSum2<> preSum(R, C, [&](int r, int c) {return mat[r][c]; });
		sort(pts.begin(), pts.end());
		long long ans = N + 1;
		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j < N; j++) {
				const int y1 = min(pts[i].second, pts[j].second);
				const int y2 = max(pts[i].second, pts[j].second);
				const long long z1 = preSum.Get(pts[i].first, 0, pts[j].first, y1);
				const long long z2 = preSum.Get(pts[i].first, y2, pts[j].first, R - 1);
				ans += z1 * z2;
			}
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0); cin.tie(nullptr);
	auto pts = Read<pair<int, int>>();
#ifdef _DEBUG		
	//printf("R=%d,C=%d",R,C);
	Out(pts, ",pts=");
	//Out(a, ",a=");
	//Out(que, ",que=");
	/*Out(que, "que=");*/
#endif // DEBUG		
	auto res = Solution().Ans(pts);
	cout << res;
	return 0;
}

单元测试

cpp 复制代码
	vector<pair<int, int>> pts;
		TEST_METHOD(TestMethod1)
		{
			pts = { {0,2},{1,0},{2,3},{3,5} };
			auto res = Solution().Ans(pts);
			AssertEx(13LL, 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++**实现。

相关推荐
rainbow7242442 小时前
AI学习路线分享:通用型认证与算法认证学习体验对比
人工智能·学习·算法
君义_noip2 小时前
信息学奥赛一本通 4163:【GESP2512七级】城市规划 | 洛谷 P14921 [GESP202512 七级] 城市规划
c++·算法·图论·gesp·信息学奥赛
Simon_lca2 小时前
验厂不翻车!Acushnet 11 项核心政策 + 自查要点,一文搞定
大数据·人工智能·经验分享·算法·制造
不想写代码的星星2 小时前
C++ 的花括号有多狂?std::initializer_list 那些不讲武德的事儿
c++
elseif1232 小时前
初学者必背【考点清单(大全)】【上篇】
开发语言·c++·笔记·学习·循环结构·分支结构·考纲
并不喜欢吃鱼2 小时前
从零开始C++----二.(下篇)模版进阶与编译全过程的复习
开发语言·c++
智者知已应修善业2 小时前
【51单片机按键控制流水灯+数码管显示按键次数】2023-6-15
c++·经验分享·笔记·算法·51单片机
汉克老师2 小时前
GESP2023年12月认证C++三级( 第三部分编程题(1、小猫分鱼))
c++·算法·模拟算法·枚举算法·gesp三级·gesp3级
不知名的老吴2 小时前
View的三大特性之一:迟绑定
开发语言·c++·算法