1. 程式人生 > 實用技巧 >【解題報告】 洛谷P6733 間歇泉

【解題報告】 洛谷P6733 間歇泉

【解題報告】 洛谷P6733 間歇泉

題目描述

有一個間歇泉,冒出了 n 杯水,由於一些原因,每杯水的溫度、體積不同。第 i 杯水的溫度為 ci體積為 ai

現在混合任意杯水,每混合兩杯水都會得到一個新的溫度值,求可能得到的第 kkk 高的溫度值(不計熱量損失)。

你的答案建議至少保留小數點後 3 位(與標準答案之差在 \(10^{-2}\) 以內即視為通過)。

輸入格式

第一行一個數 n,k,意義如題述。

接下來 n行,每行兩個數 ai,c

輸出格式

一行一個實數,表示混合後的水的第 kkk 高溫度。

輸入輸出樣例

輸入 #1

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

輸出 #1

4.500

說明/提示

樣例 1 說明

第 1 和第 5杯水混合,得到 4.5的溫度值。可以發現,這是可能得到的最高水溫。

樣例 2

見題目附件中 pour2.inpour2.ans

資料規模與約定

本題採用捆綁測試。

  • 子任務 1(9 pts):有 \(1\le n\le 10\)
  • 子任務 2(40 pts):保證 \(k=1\)
  • 子任務 3(50 pts):無特殊限制。
  • 子任務 4(1 pts):Hack 資料。

對於 100% 的資料,有 \(1\le n\le 10^5, 1\le k \le \frac {n(n-1)} {2},1\le a_i,c_i \le 10^9\)

子任務 2/3/4 時限 2s,子任務 1 時限 1s。

前置知識

對於兩杯體積、溫度分別為\((a_i,c_i),(a_j,c_j)\)的水,混合後溫度為:

\[T=\frac {a_i c_i+ a_j c_j} {a_i+c_j} \]

思路

二分。

這道題符合我們的求第k大的數的形式,因此我們可以進行二分答案來做題目。

首先分析一下這道題目:

可以混合任意兩杯水來得到溫度值,找第k大的溫度值

設這個第k大的溫度值為v

\[\frac {a_ic_i+a_jc_j} {a_i+c_j}\ge v \]

變形可以得到

\[a_ic_i-v a_i\ge va_j-a_jc_j \]

問題就是求有多少i,j滿足\(a_i\ge b_j\)

程式碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const double eps=0.001;
int n,k;
int a[100005],c[100005];
double p[100005],q[100005];
long double ans;
bool cmp(double a,double b)
{
	return a<b;
}
bool check(double v)
{
	long long tot=0;
	for(int i=1;i<=n;i++)
	{
		double x=a[i]*1.0*c[i],y=v*a[i];
		p[i]=x-y;
		q[i]=y-x;
		if(q[i]-p[i]<eps) tot--; 
	}
	sort(p+1,p+1+n,cmp);
	sort(q+1,q+1+n,cmp);
	int j=0;
	for(int i=1;i<=n;i++)
	{
		while(q[j+1]-p[i]<eps&&j+1<=n) j++;
		tot+=j;
	}
	return (tot/2<k);
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	cin>>a[i]>>c[i];
	long double l=1,r=1e9,mid;
	while(r-l>eps)
	{
		mid=(l+r)/2;
		if(check(mid)) r=mid,ans=mid;
		else l=mid;
	}
	printf("%.3Lf\n",ans);
	return 0;
}