题目大意
在X轴的正半轴上给定
n
(
1
≤
n
≤
2
e
5
)
n(1 \leq n \leq 2e5)
n(1≤n≤2e5) 个线段
l
i
,
r
i
(
1
≤
l
,
r
≤
1
e
9
)
l_i,r_i(1 \leq l, r \leq 1e9)
li,ri(1≤l,r≤1e9),并且每个线段都有一个颜色作为编号
c
i
(
1
≤
c
≤
n
)
c_i(1 \leq c \leq n)
ci(1≤c≤n)。对于每个线段,需要求出距离它最近的一个和它不同颜色的线段,并输出其距离。注意如果两条线段有交点那么距离为0。例如:
解题思路
考虑线段 a a a,设影响它答案的线段为 b b b,它们只可能出现如下几种情况:
- b b b 在 a a a 的右边,且二者无交点,即满足 r a < l b r_a < l_b ra<lb
- b b b 在 a a a 的左边,且二者无交点,即满足 r b > l a r_b > l_a rb>la
- 二者至少有一个交点
对于第一种情况,若不考虑颜色,只需要对所有线段按照左端点排序,然后二分上界找到最小的 l b l_b lb 满足 r a < l b r_a < l_b ra<lb。但是题目要求不同颜色才有效,考虑在查询时将所有和当前线段同颜色的都删掉,那么直接查询就可以了,当然删除的复杂度太大是不可取的。这时观察到一个性质,那就是如果找到了对答案有影响的线段L,L后面的线段是无用的。这时想到所有线段按颜色划分为若干个队列,将队列按左端点排序,每种颜色一次只需要丢进set一个,这个方法的正确性不难证明。做法为:首先将所有线段按颜色划分集合并将每个集合按左端点排序。循环开始前每个颜色集合将队首的线段丢入set;循环时每次取出set队首的线段Seg,将其从set删除,二分查找 r s e g r_{seg} rseg 的下界,更新完答案若Seg的同色队列非空,则将队首线段丢入set;重复上述过程直到set为空。
对于第二种情况,和上述同理,只是需要从右到左反着来。但是可以将所有线段坐标取负并交换 l i , r i l_i, r_i li,ri,这时在X轴的负半轴出现了刚好对称的线段集,和上面思路一样再计算一遍即可。
对于第三种情况,有很多方法可以做。我采用的方法是将所有线段按颜色划分集合,对于每个集合考虑差分进行区间加法,这时每个点表示的是该点被多少不同颜色的线段覆盖,因为同色线段相交的部分是无用的,在差分前要先将进行线段合并。差分后要再做一次前缀和方便去见查询,对于线段 l i , r i ( 1 ≤ l , r ≤ 1 e 9 ) l_i,r_i(1 \leq l, r \leq 1e9) li,ri(1≤l,r≤1e9),若 s u m [ r ] − s u m [ l − 1 ] > r − l + 1 sum[r] - sum[l - 1] > r - l + 1 sum[r]−sum[l−1]>r−l+1,则代表该区间至少被两种颜色的线段覆盖,答案必定为0。因为坐标范围比较大,该步骤必须对坐标离散化,比较考验基础思维和码力。
坑点
- 自定义结构体若实现set,则必须重载<运算符,当判断两个结构体对象A、B是否相等时,set执行两次<比较,即判断A<B和B<A,若两个返回结果均为false,则认为A和B是相等的。若按照下述代码的重载结构体方式使用set,如果 i n s e r t ( { 1 , 2 } ) insert(\{1, 2\}) insert({1,2}) 后 i n s e r t ( { 1 , 3 } ) insert(\{1, 3\}) insert({1,3}),后者会插入失败。要么更改运算符重载方式,要么使用multiset。
- 对差分数组做前缀和,可能会爆int。证明:前1e5个线段生成1e5个坐标,后面1e5个线段总是覆盖前面的1e5个坐标,就会爆int。
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <stack>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;
int n, m, cnt;
struct node {
int l, r, col, index;
bool operator<(const node &A) const {
return l < A.l;
}
};
vector<node> a[maxn], b[maxn];
multiset<node> s;
map<int, int> mp, pos;
int orderPos[maxn * 2];
ll insect[maxn * 2];
int ans[maxn];
int getID(int i) {
if(!mp.count(i))
mp[i] = ++m;
return mp[i];
}
void add(int l, int r) {
insect[pos[l]]++;
insect[pos[r] + 1]--;
}
ll ask(int l, int r) {
return insect[pos[r]] - insect[pos[l] - 1];
}
void work1() {
for(int i = 1; i <= m; i++) {
sort(a[i].begin(), a[i].end());
reverse(a[i].begin(), a[i].end());
s.insert(*a[i].rbegin());
a[i].pop_back();
}
while(!s.empty()) {
auto cur = *s.begin();
s.erase(s.begin());
auto now = s.lower_bound({cur.r, cur.l, 0, 0});
if(now != s.end()) {
ans[cur.index] = min(ans[cur.index], (*now).l - cur.r);
ans[(*now).index] = min(ans[(*now).index], (*now).l - cur.r);
}
if(!a[cur.col].empty()) {
s.insert(*a[cur.col].rbegin());
a[cur.col].pop_back();
}
}
}
void work2() {
for(int i = 1; i <= m; i++) {
sort(b[i].begin(), b[i].end());
reverse(b[i].begin(), b[i].end());
s.insert(*b[i].rbegin());
b[i].pop_back();
}
while(!s.empty()) {
auto cur = *s.begin();
s.erase(s.begin());
auto now = s.lower_bound({cur.r, cur.l, 0, 0});
if(now != s.end()) {
ans[cur.index] = min(ans[cur.index], (*now).l - cur.r);
ans[(*now).index] = min(ans[(*now).index], (*now).l - cur.r);
}
if(!b[cur.col].empty()) {
s.insert(*b[cur.col].rbegin());
b[cur.col].pop_back();
}
}
}
void solve() {
for(int i = 1; i <= m; i++) {
sort(a[i].begin(), a[i].end());
int l = a[i][0].l, r = a[i][0].r;
for(int j = 1; j < a[i].size(); j++) {
if(a[i][j].l > r) {
add(l, r);
l = a[i][j].l;
r = a[i][j].r;
} else {
r = max(r, a[i][j].r);
}
}
add(l, r);
}
for(int i = 1; i <= cnt; i++) insect[i] += insect[i - 1];
for(int i = 1; i <= cnt; i++) insect[i] += insect[i - 1];
for(int i = 1; i <= m; i++) {
for(auto p : a[i]) {
if(ask(p.l, p.r) > pos[p.r] - pos[p.l] + 1) {
ans[p.index] = 0;
}
}
}
work1();
work2();
for(int i = 1; i < n; i++)
cout << ans[i] << " ";
cout << ans[n] << "\n";
}
void pre() {
cin >> n;
m = cnt = 0;
mp.clear();
pos.clear();
for(int i = 1, x, y, z, col_id; i <= n; i++) {
cin >> x >> y >> z;
col_id = getID(z);
if(!pos.count(x)) {
orderPos[++cnt] = x;
pos[x] = 1;
}
if(!pos.count(y)) {
orderPos[++cnt] = y;
pos[y] = 1;
}
a[col_id].push_back({x, y, col_id, i});
b[col_id].push_back({-y, -x, col_id, i});
}
sort(orderPos + 1, orderPos + cnt + 1);
for(int i = 1; i <= cnt; i++) pos[orderPos[i]] = i;
memset(insect, 0, sizeof insect);
memset(ans, 0x3f, sizeof ans);
solve();
}
int main() {
int T;
cin >> T;
while(T--) {
pre();
}
return 0;
}