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}} 10−2 以内即视为通过)。
第一行一个数 n , k n,k n,k,意义如题述。
接下来 n n n 行,每行两个数 a i , c i a_i,c_i ai,ci。
一行一个实数,表示混合后的水的第 k k k 高温度。
5 1
1 5
4 2
5 3
2 3
1 4
4.500
第 1 1 1 和第 5 5 5 杯水混合,得到 4.5 4.5 4.5 的温度值。可以发现,这是可能得到的最高水温。
见题目附件中 pour2.in/pour2.ans \textbf{\textit{pour2.in/pour2.ans}} pour2.in/pour2.ans。
本题采用捆绑测试。
对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105, 1 ≤ k ≤ n × ( n − 1 ) 2 1\le k\le \dfrac{n \times (n - 1)}{2} 1≤k≤2n×(n−1), 1 ≤ a i , c i ≤ 1 0 9 1\le a_i,c_i\le 10^9 1≤ai,ci≤109。
子任务 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。
是个分数,但肯定有误差,考虑缩小误差,因为
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
我们二分一个温度double d,需找到现在有多少杯水温度 ≥ d \geq d ≥d,后记录下,判断即可:
关于判断,这里写下公式:
T ≥ d → a i c i + a j c j a i + a j ≥ d → a i c i + a j c j ≥ d a i + d a j T \ge d \to \dfrac{a_ic_i+a_jc_j}{a_i+a_j} \ge d \to {a_ic_i+a_jc_j} \ge d{a_i+da_j} T≥d→ai+ajaici+ajcj≥d→aici+ajcj≥dai+daj
进一步整理得到:
a i c i − d a i ≥ d a j − a j c j a_ic_i-da_i \ge da_j-a_jc_j aici−dai≥daj−ajcj
此时发现可以使用换元法替换为:
x i − y i > = y j − x j x_i-y_i>=y_j-x_j xi−yi>=yj−xj
左右两个值可以存在数组中,用双指针选出答案
#include
using namespace std;
const int M = 1e5+10;
const double eps = 1e-3;
int n, k;
int a[M], c[M];
double p[M], q[M];
function<bool(double,double)> cmp = [](double x, double y) {return x < y; };
void read() {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i] >> c[i];
}
bool check(double d) {
int tot = 0;
for (int i = 1; i <= n; i++) {
double x = a[i] * 1.0 * c[i], y = d * a[i];
p[i] = x - y, q[i] = y - x;
if (q[i] - p[i] < eps) tot--;
}
sort(p + 1, p + n + 1, cmp);
sort(q + 1, q + n + 1, cmp);
for (int i = 1, j = 0; i <= n; i++) {
while (q[j + 1] - p[i] < eps and j + 1 <= n) j++;
tot += j;
}
tot /= 2;
return tot < k;
}
void solve() {
double l = 0, r = 1e11, mid,ans;
while (r - l > eps) {
mid = (l + r) / 2;
if (check(mid)) ans = mid, r = mid;
else l = mid;
}
printf("%.3f", ans);
}
int main() {
read();
solve();
return 0;
}
在上述分析中已说明思路,在此补充细节
if (q[i] - p[i] < eps) tot--;
不能与自己混合
for (int i = 1, j = 0; i <= n; i++) {
while (q[j + 1] - p[i] < eps and j + 1 <= n) j++;
tot += j;
}
x i − y i ≥ y j − x j → p [ i ] ≥ q [ j ] → 0 ≥ q [ j ] − p [ i ] x_i-y_i \ge y_j-x_j \to p[i] \ge q[j] \to 0 \ge q[j]-p[i] xi−yi≥yj−xj→p[i]≥q[j]→0≥q[j]−p[i]