P2782 友好城市 LIS / NLOGN

解题思路

  1. 问题分析:

    • 我们需要选择尽可能多的友好城市对,使得它们的航线不相交。

    • 航线不相交的条件是:如果北岸城市A在北岸城市B的左边,那么A对应的南岸城市也必须在南岸城市B的左边。

  2. 关键观察:

    • 将北岸城市按坐标排序后,南岸对应的城市坐标必须是一个严格递增的序列,才能保证航线不相交。

    • 因此,问题转化为:对南岸城市坐标求最长上升子序列(LIS)。

  3. 算法选择:

    • 使用O(nlogn)的LIS算法,因为数据规模较大(N≤2×10^5)。

代码注释

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, inf = 0x3f3f3f3f;

// 定义城市结构体,存储南北岸坐标
struct node {
    int x, y; // x:北岸坐标,y:南岸坐标
};

node a[N]; // 存储所有城市对
int n;     // 城市数量
int f[N];  // f数组用于维护LIS
int top;   // 当前LIS长度

// 比较函数:按北岸坐标升序排序
bool cmp(node a, node b) {
    return a.x < b.x;
}

// 二分查找函数:找到第一个大于等于x的位置
int find(int x) {
    int l = 1, r = top, res = -1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (x <= f[mid]) { // 找到更小的候选值
            res = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    return res;
}

int main() {
    cin >> n;
    // 输入所有城市对
    for (int i = 1; i <= n; i++) 
        cin >> a[i].x >> a[i].y;
    
    // 按北岸坐标升序排序
    sort(a + 1, a + 1 + n, cmp);
    
    // 初始化f数组为极大值
    memset(f, inf, sizeof(f));
    
    // 计算LIS
    for (int i = 1; i <= n; i++) {
        int pos = find(a[i].y); // 找到插入位置
        if (pos == -1) {
            // 如果当前值比所有都大,扩展LIS
            f[++top] = a[i].y;
        } else {
            // 否则替换第一个大于等于它的值
            f[pos] = a[i].y;
        }
    }
    
    // 输出最长不下降子序列长度
    cout << top;
    return 0;
}

 

posted @ 2025-06-15 11:33  CRt0729  阅读(15)  评论(0)    收藏  举报