CF580B Kefa and Company 题解 双指针/二分

题目链接:http://codeforces.com/problemset/problem/580/B

题目描述

聪聪最近领到了工资所以他决定去五星级大饭店大吃一顿,但是一个人吃不爽,他需要朋友的陪伴。
聪聪有 (n) 个朋友,如果聪聪邀请这 (n) 个朋友中的任何一个人去吃饭,他们都会有时间陪伴聪聪去吃饭。
聪聪的每个朋友都有有两个参数,对于第 (i) 个朋友,它的这两个参数表示如下:

  1. 富裕指数(m_i):这个参数表示第 (i) 个朋友多有钱,富裕指数越高表示这个朋友越有钱;
  2. 友情因子(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;
}
原文地址:https://www.cnblogs.com/quanjun/p/12206213.html