【二分查找 双指针】P6733 「Wdsr-2」间歇泉|普及+

本文涉及的基础知识点

C++二分查找
C++算法:滑动窗口及双指针总结

P6733 「Wdsr-2」间歇泉

题目背景

Problem Number: 08 \textit{08} 08

题目描述

有一个间歇泉,冒出了 n n n 杯水,由于一些原因,每杯水的温度、体积不同。第 i i i 杯水的温度为 c i c_i ci,体积为 a i a_i ai

现在混合任意杯水,每混合两杯水都会得到一个新的温度值,求可能得到的第 k k k 高的温度值(不计热量损失)。

你的答案建议至少保留小数点后 3 \bm3 3 位(与标准答案之差在 1 0 − 2 \bm{10^{-2}} 102 以内即视为通过)。

输入格式

第一行一个数 n , k n,k n,k,意义如题述。

接下来 n n n 行,每行两个数 a i , c i a_i,c_i ai,ci

输出格式

一行一个实数,表示混合后的水的第 k k k 高温度。

输入输出样例 #1

输入 #1

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

输出 #1

4.500

说明/提示

样例 1 说明

1 1 1 和第 5 5 5 杯水混合,得到 4.5 4.5 4.5 的温度值。可以发现,这是可能得到的最高水温。

样例 2

见题目附件中 pour2.in/pour2.ans \textbf{\textit{pour2.in/pour2.ans}} pour2.in/pour2.ans

数据规模与约定

本题采用捆绑测试。

  • Subtask   1  (10 pts) \textbf{Subtask 1}\text{ (10 pts)} Subtask 1 (10 pts) 1 ≤ n ≤ 10 1\le n\le 10 1n10
  • Subtask   2  (40 pts) \textbf{Subtask 2}\text{ (40 pts)} Subtask 2 (40 pts):保证 k = 1 k=1 k=1
  • Subtask   3  (50 pts) \textbf{Subtask 3}\text{ (50 pts)} Subtask 3 (50 pts):无特殊限制。
  • Subtask   4  (0 pts) \textbf{Subtask 4}\text{ (0 pts)} Subtask 4 (0 pts):Hack 数据。

对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1n105 1 ≤ k ≤ n × ( n − 1 ) 2 1\le k\le \dfrac{n \times (n - 1)}{2} 1k2n×(n1) 1 ≤ a i , c i ≤ 1 0 9 1\le a_i,c_i\le 10^9 1ai,ci109

子任务 2/3/4 时限 2 s \text{2 s} 2 s,子任务 1 时限 1 s \text{1 s} 1 s

前置知识

对于两杯体积、温度分别为 ( a i , c i ) , ( a j , c j ) (a_i,c_i),(a_j,c_j) (ai,ci),(aj,cj) 的水,混合后温度为:

T = a i c i + a j c j a i + a j T=\dfrac{a_ic_i+a_jc_j}{a_i+a_j} T=ai+ajaici+ajcj

说明

本题数据采用 SvRan 生成,仅用时 3 min ⁡ 3\min 3min

P6733 「Wdsr-2」间歇泉

Cnt函数

某杯水体积a1,温度c1,a1和c1是已知数。另一杯水体积是a,温度是c,未知数。
判断合并后温度是否大于已知数v: (a1c1+ac)/(a1+a)>=v ,则 a1c1+ac >= a1v+av即 ac-av>=a1v -a1c1。
如果数据已经按a
c-av排序,直接二分。注意;第i杯水和第j杯水混合。 i ≠ j i\neq j i=j,(i,j)和(j,i)相同,故除以2。 时间复杂度:O(logn) 求所有(a1,c1)和其它水混合的时间复杂度是n(logn)
二分类型:寻找尾端
Check(mid)函数:混合后,温度大于等于mid的杯数大于等于k。先按a
c-a*v排序,在调用Cnt函数。
参数范围:[1,1e9]
精度:0.004
时间复杂度:O(logMnlogn)
可以将温度上调1000倍,二分[1,112],然后除以1000。运算过程没有误差。温度在long long范围内。

代码

核心代码

#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;
}

class CNeiBo
{
public:
	static vector<vector<int>> Two(int n, const vector<pair<int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& [i1, i2] : edges)
		{
			vNeiBo[i1 - iBase].emplace_back(i2 - iBase);
			if (!bDirect)
			{
				vNeiBo[i2 - iBase].emplace_back(i1 - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Two(int n, const vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}
	static vector<vector<std::pair<int, int>>> Three(int n, const vector<tuple<int, int, int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& [u, v, w] : edges)
		{
			vNeiBo[u - iBase].emplace_back(v - iBase, w);
			if (!bDirect)
			{
				vNeiBo[v - iBase].emplace_back(u - iBase, w);
			}
		}
		return vNeiBo;
	}
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex, INDEX_TYPE tol = 1) :m_iMin(iMinIndex), m_iMax(iMaxIndex), m_iTol(tol) {}
	template<class _Pr>
	INDEX_TYPE FindFrist(_Pr pr)
	{
		auto left = m_iMin - m_iTol;
		auto rightInclue = m_iMax;
		while (rightInclue - left > m_iTol)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd(_Pr pr)
	{
		INDEX_TYPE leftInclude = m_iMin;
		INDEX_TYPE right = m_iMax + m_iTol;
		while (right - leftInclude > m_iTol)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax, m_iTol;
};

class Solution {
	public:
		long long Ans(int K, vector<pair<int, long long>>& vc) {
			const int N = vc.size();
			for (auto& [v, c] : vc) { c *= 1000; }
			auto Cmp = [&](const pair<int, int>& pr, long long mid) {return (long long)pr.first * pr.second - pr.first * mid; };
			auto Check = [&](long long mid) {
				sort(vc.begin(), vc.end(), [&](const pair<int, int>& pr1, const pair<int, int>& pr2) {
					return Cmp(pr1, mid) < Cmp(pr2, mid);  });
				vector<long long> cmps(N);
				for (int i = 0; i < N; i++) {
					cmps[i] = Cmp(vc[i], mid);
				}
				long long cnt = 0;
				for (const auto& pr : vc) {
					const auto needMoreEqual = -Cmp(pr, mid);
					auto it = lower_bound(cmps.begin(), cmps.end(), needMoreEqual);
					cnt += cmps.end() - it;
					cnt -= (-needMoreEqual >= needMoreEqual);//不能和自己混合
				}
				return cnt / 2 >= K;
			};
			return CBinarySearch<long long>(1, 1e12).FindEnd(Check);
		}
	};


int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0); cin.tie(nullptr);
	int N, K;
	cin >> N >> K;
	auto vc = Read<pair<int, long long>>(N);
#ifdef _DEBUG		
	//printf("K=%d",K);
	//cout << ",s=" << s;
	//Out(vc, ",vc=");
	//Out(a, ",a=");
	//Out(que, ",que=");
	/*Out(que, "que=");*/
#endif // DEBUG		
	auto res = Solution().Ans(K,vc);
	auto tmp = res / 1000.0;
	printf("%lf\n", tmp);
	return 0;
}

单元测试

int K;
		vector<pair<int, long long>> vc;
		TEST_METHOD(TestMethod1)
		{
			K = 1, vc = { {1,5},{4,2},{5,3},{2,3},{1,4} };
			auto res = Solution().Ans(K,vc);
			AssertEx(4500LL, 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++**实现。

评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件架构师何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值