算法
听别人说这题比较简单, 我来看看怎么个事
转化题意,
对于 \(n\) 条线段 \([l_i, r_i]\) , 求每条线段被哪些线段完全覆盖, 并求这些线段的交集减去其的长度
显然的, \(j\) 线段覆盖 \(i\) 线段, 应该满足 \(l_j \leq l_i, r_i \leq r_j\)
那么我们考虑将每一条线段当做一个点处理, 放到以 \(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();
}
总结
善于转化问题
注意问题到底需要求最值的哪边
标签:Educational,Rated,int,线段,Codeforces,xx,mp,ans,id From: https://www.cnblogs.com/YzaCsp/p/18585039