CF580B Kefa and Company 题解 双指针/二分
题目链接:http://codeforces.com/problemset/problem/580/B
题目描述
聪聪最近领到了工资所以他决定去五星级大饭店大吃一顿,但是一个人吃不爽,他需要朋友的陪伴。
聪聪有 \(n\) 个朋友,如果聪聪邀请这 \(n\) 个朋友中的任何一个人去吃饭,他们都会有时间陪伴聪聪去吃饭。
聪聪的每个朋友都有有两个参数,对于第 \(i\) 个朋友,它的这两个参数表示如下:
- 富裕指数\(m_i\):这个参数表示第 \(i\) 个朋友多有钱,富裕指数越高表示这个朋友越有钱;
- 友情因子\(s_i\):这个参数表示如果第 \(i\) 个朋友陪伴聪聪一起去吃饭,聪聪能够提升的幸福值。
聪聪想要他的幸福值提升地尽可能地大。
但是考虑到他的朋友的富裕指数不同,所以如果他的朋友之间贫富差距过大的话就会出现“贫穷鄙视”。
如果对于两个来吃饭的朋友 \(i\) 和 \(j\) ,他们的富裕指数之差 \(m_i - m_j \gt d\) ,就会出现“贫穷鄙视”,这会让朋友 \(j\) 觉得自己很穷而自卑。
聪聪想在不出现“贫穷鄙视”的情况下获得最多的幸福值。
他能够获得的幸福值就是所有来吃饭的朋友的友情因子之和。
请帮他计算一下他能够获得的最大幸福值是多少!
输入格式
输入的第一行包含两个正数 \(n\) 和 \(d\) ( \(1 \le n \le 10^5\) ,\(1 \le d \le 10^9\) ),分别表示聪聪朋友的个数和富裕指数的边界值。
接下来 \(n\) 行用于描述聪聪的朋友,第 \(i+1\) 行包含两个参数 \(m_i\) 和 \(s_i\)(\(0 \le m_i,s_i \le 10^9\)),对应第 \(i\) 个朋友的富裕指数和友情因子。
输出格式
输出聪聪所能够获得的最大的幸福值。
问题分析
首先我们要考虑一下给我这道题我们怎么去解决。
对于这道题,我们可以先给 \(n\) 个朋友按照 \(m_i\) 值从小到大排个序。
排完序之后这道题目就简化成了:我们要找两个坐标 \(i\) 和 \(j\) ,使得 \(m_j - m_i < d\) 并且 \(\sum(s_i \cdots s_j)\) 最大。(这里 \(\sum(s_i \cdots s_j)\) 表示 第 \(i\)、\(i+1\)、……、\(j\) 个朋友的友情因子之和 )
然后到了这里老师就想到了两种方法来解决这个问题。
第一种方法是采用双指针的方式来解决这个问题,时间复杂度是 \(O(n)\) 。
实现思路是:一开始设一个变量 \(j = 0\) , 然后从 \(0\) 到 \(n-1\) 遍历 \(i\) ,对于每一个 \(i\) ,如果 \(m_i-m_j \ge d\) ,就执行 \(j ++\) 。然后从 \(j\) 到 \(i\) 这个范围的朋友的友情因子之和就是答案的一个候选值。
而对于 第 \(j\) 到 第 \(i\) 个好友的友情因子之和,我们可以在一开始用一个 \(sum[]\) 数组初始化一下( \(sum[i]=sum[i-1]+s[i]\) ),那么我们就可以以 \(O(1)\) 的时间复杂度求得从 \(j\) 到 \(i\) 这个范围的朋友的友情因子之和 为 \(sum[i] - sum[j-1]\) 。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n;
long long d, sum[maxn];
struct Node {
long long m, s;
} a[maxn];
bool cmp(Node a, Node b) {
return a.m < b.m;
}
int main() {
cin >> n >> d;
for (int i = 0; i < n; i ++) cin >> a[i].m >> a[i].s;
sort(a, a+n, cmp);
for (int i = 0; i < n; i ++) sum[i] = a[i].s + ( i ? sum[i-1] : 0 );
int j = 0;
long long ans = 0LL;
for (int i = 0; i < n; i ++) {
while (a[i].m - a[j].m >= d) j ++;
ans = max(ans, sum[i] - ( j ? sum[j-1] : 0 ) );
}
cout << ans << endl;
return 0;
}
第二种方法是使用二分。
因为聪聪的 \(n\) 个朋友按照 \(m_i\) 值从小到大排了序,所以 \(m_i\) 具有单调性。
对于 第 \(i\) 个朋友,我们可以对区间 \([i, n]\) 范围内的朋友进行二分查找,找到最大的那个满足 \(mj-mi \lt d\) 的 \(j\) ,这个时候 \(sum[j] - sum[i-1]\) 就是我们的一个候选答案。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n;
long long d, sum[maxn];
struct Node {
long long m, s;
} a[maxn];
bool cmp(Node a, Node b) {
return a.m < b.m;
}
int main() {
cin >> n >> d;
for (int i = 0; i < n; i ++) cin >> a[i].m >> a[i].s;
sort(a, a+n, cmp);
for (int i = 0; i < n; i ++) sum[i] = a[i].s + ( i ? sum[i-1] : 0 );
long long ans = 0LL;
for (int i = 0; i < n; i ++) {
int L = i, R = n-1, res;
while (L <= R) {
int mid = (L + R) / 2;
if (a[mid].m - a[i].m < d) {
res = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
ans = max(ans, sum[res] - (i ? sum[i-1] : 0));
}
cout << ans << endl;
return 0;
}