Educational Codeforces Round 172 (Rated for Div. 2) D. Recommendations

算法#

听别人说这题比较简单, 我来看看怎么个事

转化题意,
对于 n 条线段 [li,ri] , 求每条线段被哪些线段完全覆盖, 并求这些线段的交集减去其的长度

显然的, j 线段覆盖 i 线段, 应该满足 ljli,rirj

那么我们考虑将每一条线段当做一个点处理, 放到以 l 为横轴, r 为纵轴的矩阵上, 那么问题就转化成二维矩阵上, 求最靠近右下角的一个点的横纵坐标, 具体的转化是显然的, 那么我们就可以用扫描线算法中的经典问题: 二维数点来做

这下变成板子题了, 那么我们去学新算法吧

关于二维数点 & 扫描线#

扫描线实际上就是暴力枚举一维, 另一维用数据结构维护, 以此来做到优化时间复杂度

但是二维数点这种问题怎么办?

我们考虑离线询问, 按照一维排序, 那么对于另一个维度, 树状数组区间和即可


那么对于这道题, 我们怎么去处理

首先假设按照 l 排序, 为了保证正确性, 还需要按照 r 从大到小排序, 确保当前节点处理时, 所有在左上角矩阵中的点都已经处理过, 可以使用树状数组, 以 r 为下标维护 l 的最大值, 而 r 的最小值可以二分求出, 仅仅是比当前的大于或等于即可

代码#

#include <bits/stdc++.h>
using namespace std;
const int m = 1e9 + 2;

int n, s[200005];

struct su
{
    int x, y, id;
    bool operator<(const su &temp) const
    {
        if (x == temp.x)
            return y > temp.y;
        return x < temp.x;
    }
} a[200005];
int ans[200005];

void add(int x, int v)
{
    for (; x <= n; x += x & -x)
        s[x] = max(s[x], v);
}

int ask(int x)
{
    int res = 0;
    for (; x; x -= x & -x)
        res = max(s[x], res);
    return res;
}

void solve()
{
    cin >> n;
    set<int> q;
    vector<int> f;
    unordered_map<int, int> mp;
    for (int i = 1; i <= n; ++i)
    {
        int x, y;
        cin >> x >> y;
        a[i].x = x;
        a[i].y = y;
        a[i].id = i;
        f.push_back(a[i].y); // 按照 x 进行一个扫, 把 y 加入
        s[i] = 0;
    }
    sort(f.begin(), f.end());
    reverse(f.begin(), f.end());
    unique(f.begin(), f.end());
    for (int i = 0; i < f.size(); ++i) // 离散化
        mp[f[i]] = i + 1;
        
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++)
    {
        if (i > 1)
        {
            q.insert(a[i - 1].y);
            add(mp[a[i - 1].y], a[i - 1].x);
        }
        if (i < n && a[i].x == a[i + 1].x && a[i].y == a[i + 1].y)
        {
            ans[a[i].id] = 0;
            continue;
        }
        auto xx = q.lower_bound(a[i].y);
        if (xx == q.end())
        {
            ans[a[i].id] = 0;
            continue;
        }
        int y = ask(mp[a[i].y]);
        if ((*xx) < y)
        {
            ans[a[i].id] = 0;
            continue;
        }
        ans[a[i].id] = (*xx) - y - (a[i].y - a[i].x);
    }
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i] << "\n";
    }
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();
}

总结#

善于转化问题

注意问题到底需要求最值的哪边

posted @   Yorg  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示