【排序 离散化 二维前缀和】 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++**实现。

相关推荐
tankeven3 小时前
C++ 算法类
c++
合兴软件@4 小时前
合兴软件重磅推出高性能HSM固件 国密算法赋能汽车信息安全新防线
网络·算法·网络安全·汽车·信息与通信
wearegogog1234 小时前
基于遗传算法的阵列天线方向图优化MATLAB实现
算法·matlab
挨踢ren4 小时前
C++ std::function:万能函数包装器
c++
Controller-Inversion4 小时前
312. 戳气球
算法
图码4 小时前
最大子数组和问题:从暴力到Kadane算法的优雅蜕变
数据结构·算法·动态规划·柔性数组
Raink老师4 小时前
用100道题拿下你的算法面试(链表篇-5):删除链表的倒数第 N 个节点
算法·链表·面试
qq_296553274 小时前
[特殊字符] 数组中的递增三元组:O(n) 时间高效查找,面试必考!
数据结构·算法·面试·职场和发展·组合模式·柔性数组
今儿敲了吗4 小时前
链表篇(一)——合并两个有序链表
数据结构·笔记·算法·链表
fie88894 小时前
基于BBO算法的网络负载均衡优化(MATLAB实现)
网络·算法·负载均衡