F. Multi-Colored Segments

F. Multi-Colored Segments

Dmitry has $n$ segments of different colors on the coordinate axis $Ox$. Each segment is characterized by three integers $l_i$, $r_i$ and $c_i$ $(1 \leq l_i \leq r_i \leq {10}^{9},1 \leq c_i \leq n)$, where $l_i$ and $r_i$ are are the coordinates of the ends of the $i$-th segment, and $c_i$ is its color.

Dmitry likes to find the minimum distances between segments. However, he considers pairs of segments of the same color uninteresting. Therefore, he wants to know for each segment the distance from this segment to the nearest differently colored segment.

The distance between two segments is the minimum of the distances between a point of the first segment and a point of the second segment. In particular, if the segments intersect, then the distance between them is equal to $0$.

For example, Dmitry has $5$ segments:

  • The first segment intersects with the second (and these are segments of different colors), so the answers for them are equal to $0$.
  • For the $3$-rd segment, the nearest segment of a different color is the $2$-nd segment, the distance to which is equal to $2$.
  • For the $4$-th segment, the nearest segment of a different color is the $5$-th segment, the distance to which is equal to $1$.
  • The $5$-th segment lies inside the $2$-nd segment (and these are segments of different colors), so the answers for them are equal to $0$.

Input

The first line of the input contains an integer $t$ $(1 \leq t \leq {10}^{4})$ — the number of test cases in the test.

The descriptions of the test cases follow.

The first line of description of each test case contains one integer $n$ $(2 \leq n \leq 2 \cdot {10}^{5})$ — the number of segments.

The next $n$ lines contain descriptions of the segments. Each segment is described by three integers $l_i$, $r_i$ and $c_i$ $(1 \leq l_i \leq r_i \leq {10}^{9},1 \leq c_i \leq n)$ — coordinates of the left and right ends of $i$-th segment, as well as the color of this segment. It is guaranteed that there are at least $2$ segments of different colors.

It is guaranteed that the sum of $n$ over all test cases does not exceed $2 \cdot {10}^{5}$.

Output

For each test case, on a separate line print $n$ integers, where the $i$-th number is equal to the distance from the $i$-th segment to the nearest segment of a different color.

Examples

input

9
3
1 2 1
3 4 1
5 6 2
2
100000000 200000000 1
900000000 1000000000 2
5
1 2 1
2 3 2
3 4 3
4 5 4
5 6 5
5
1 5 1
4 9 2
1 2 1
8 9 2
5 7 3
2
1 100 2
10 90 1
3
1 1 1
10 10 2
1000000000 1000000000 3
3
3 4 1
2 5 1
1 6 2
6
5 6 2
11 12 3
7 8 2
3 4 2
1 2 1
9 10 2
2
1 3 1
2 3 2

output

3 1 1 
700000000 700000000 
0 0 0 0 0 
0 0 2 1 0 
0 0 
9 9 999999990 
0 0 0 
3 1 3 1 1 1 
0 0 

input

4
8
11 16 7
12 15 7
2 5 8
17 22 5
1 8 8
19 23 8
16 16 6
6 7 5
9
1 4 3
5 11 1
8 11 3
1 10 1
2 11 1
1 10 4
3 11 1
5 7 1
1 11 1
9
25 25 1
26 26 1
24 24 2
13 14 2
12 16 2
17 18 1
19 19 1
24 27 2
24 27 1
9
15 18 1
20 22 2
13 22 2
13 22 2
3 13 2
6 10 2
3 6 2
19 24 2
22 24 2

output

0 1 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 3 1 1 3 0 0 
0 2 0 0 2 5 9 1 4 

 

解题思路

  我们先考虑最简单的情况,假设区间没有颜色,求每个区间与其余区间的最短距离。

  首先需要判断是否存在其他区间与当前的区间有交集。要解决这个问题可以用差分与前缀和,我们对每一个区间内都加上$1$,然后对于某个区间$[l, r]$如果有$s_r - s_{l - 1} > r - l + 1$,说明至少存在一个区间与当前区间有交集。这是一种解决方案,同时还可以用值域线段树(需要离散化),原理也是一样的。因为后面会涉及到区间的修改,因此这里用线段树来解决这个问题。

  然后是如果不存在其他区间与当前区间有交集,那么如何求出当前区间与剩余区间的最短距离?我们可以开两个$\text{std::multiset}$,$\text{sl}$和$\text{sr}$来分别存储每个区间的左右端点(不用$\text{std::set}$而用$\text{std::multiset}$是因为会存在值相同的端点),对于当前区间$[l,r]$,要找到左边距离它最近的一个区间右端点就是在$\text{sr}$中找到小于$l$的最大的数,即 *(--sr.lower_bound(l)) 。同理要找到右边距离它最近的一个区间左端点就是在$\text{sl}$中找到大于$r$的最小的数,即 *sl.lower_bound(r) 。

  如果要考虑颜色的话呢?其实这个做法也是比较暴力的,我们可以枚举每一种颜色,然后把所有是当前颜色的区间删除,具体的操作是:在线段树中对应的区间都减去$1$,同时在$\text{sl}$和$\text{sr}$中把对应的左右端点删掉。此时维护的是的区间颜色不是当前颜色的区间,这时再枚举所有是当前颜色的区间(即已被删掉的区间),问题就变成了和上面假设的简单问题一样了(因为当前维护的区间与被删掉的区间颜色不同,因此可以看作是所有区间都没有颜色,被删掉的区间可以与当前维护的区间求距离)。最后记得要把被删除的区间信息重新加回来,继续枚举下一种颜色。

  AC代码如下:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int N = 2e5 + 10, M = N * 2;
  5 
  6 struct Seg {
  7     int l, r, c;
  8 }seg[N];
  9 struct Node {
 10     int l, r;
 11     int s, add;
 12 }tr[M * 4];
 13 int xs[M], sz;
 14 vector<int> color[N];    // color[i]存储的是所有颜色为i的区间编号
 15 multiset<int> sl, sr;    // 存储每个区间的左右端点
 16 int ans[N];
 17 
 18 int find(int x) {
 19     int l = 1, r = sz;
 20     while (l < r) {
 21         int mid = l + r >> 1;
 22         if (xs[mid] >= x) r = mid;
 23         else l = mid + 1;
 24     }
 25     return l;
 26 }
 27 
 28 void pushdown(int u) {
 29     if (tr[u].add) {
 30         tr[u << 1].add += tr[u].add;
 31         tr[u << 1].s += (tr[u << 1].r - tr[u << 1].l + 1) * tr[u].add;
 32         tr[u << 1 | 1].add += tr[u].add;
 33         tr[u << 1 | 1].s += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * tr[u].add;
 34         tr[u].add = 0;
 35     }
 36 }
 37 
 38 void build(int u, int l, int r) {
 39     if (l == r) {
 40         tr[u] = {l, r, 0};
 41     }
 42     else {
 43         int mid = l + r >> 1;
 44         build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
 45         tr[u] = {l, r, 0};
 46     }
 47 }
 48 
 49 void modify(int u, int l, int r, int c) {
 50     if (tr[u].l >= l && tr[u].r <= r) {
 51         tr[u].s += (tr[u].r - tr[u].l + 1) * c;
 52         tr[u].add += c;
 53     }
 54     else {
 55         pushdown(u);
 56         int mid = tr[u].l + tr[u].r >> 1;
 57         if (l <= mid) modify(u << 1, l, r, c);
 58         if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
 59         tr[u].s = tr[u << 1].s + tr[u << 1 | 1].s;
 60     }
 61 }
 62 
 63 int query(int u, int l, int r) {
 64     if (tr[u].l >= l && tr[u].r <= r) return tr[u].s;
 65     pushdown(u);
 66     int mid = tr[u].l + tr[u].r >> 1, s = 0;
 67     if (l <= mid) s = query(u << 1, l, r);
 68     if (r >= mid + 1) s += query(u << 1 | 1, l, r);
 69     return s;
 70 }
 71 
 72 void solve() {
 73     int n;
 74     scanf("%d", &n);
 75     
 76     sz = 0;
 77     for (int i = 1; i <= n; i++) {
 78         color[i].clear();
 79     }
 80     sl.clear(), sr.clear();
 81     
 82     for (int i = 1; i <= n; i++) {
 83         int l, r, c;
 84         scanf("%d %d %d", &l, &r, &c);
 85         seg[i] = {l, r, c};
 86         xs[++sz] = l, xs[++sz] = r;
 87         color[c].push_back(i); 
 88     }
 89     
 90     sort(xs + 1, xs + sz + 1);
 91     sz = unique(xs + 1, xs + sz + 1) - xs - 1;
 92     
 93     build(1, 1, sz);
 94     for (int i = 1; i <= n; i++) {    // 先维护每个区间的信息
 95         int l = find(seg[i].l), r = find(seg[i].r);
 96         modify(1, l, r, 1);    // [l, r]都加上1
 97         sl.insert(l), sr.insert(r);    // 左端点l加到sl,右端点r加到sr
 98     }
 99     
100     for (int i = 1; i <= n; i++) {    //枚举每一种颜色
101         for (int &j : color[i]) {    // 把颜色为i的区间信息都删除
102             int l = find(seg[j].l), r = find(seg[j].r);
103             modify(1, l, r, -1);    // [l, r]都减1
104             // 注意,不可以写成sl.erase(l), sr.erase(r);
105             // 这个是删除所有值为l/r的元素!
106             // 如果只是要删除这个数,应该删除的是迭代器
107             // 这里debug了半天...
108             sl.erase(sl.lower_bound(l)), sr.erase(sr.lower_bound(r));    // 删除区间的左右端点
109         }
110         
111         for (auto &j : color[i]) {    // 再枚举被删除的区间
112             int l = find(seg[j].l), r = find(seg[j].r);
113             if (query(1, l, r)) {    // 如果区间[l, r]的和大于0,意味着存在不同颜色的区间与当前区间有交集
114                 ans[j] = 0;
115             }
116             else {
117                 // a是左边小于r的最大的数(减减后,即前一个位置),b是右边大于l的最小的数
118                 auto a = sr.lower_bound(l), b = sl.lower_bound(r);
119                 ans[j] = 2e9;
120                 if (a != sr.begin()) ans[j] = xs[l] - xs[*--a];
121                 if (b != sl.end()) ans[j] = min(ans[j], xs[*b] - xs[r]);
122             }
123         }
124         
125         for (int &j : color[i]) {    // 再把被删除的区间信息加回来
126             int l = find(seg[j].l), r = find(seg[j].r);
127             modify(1, l, r, 1);
128             sl.insert(l), sr.insert(r);
129         }
130     }
131     
132     for (int i = 1; i <= n; i++) {
133         printf("%d ", ans[i]);
134     }
135     printf("\n");
136 }
137 
138 int main() {
139     int t;
140     scanf("%d", &t);
141     while (t--) {
142         solve();
143     }
144     
145     return 0;
146 }

  来算一下时间复杂度,虽然看着是两重循环:

for (int i = 1; i <= n; i++) {
    for (int &j : color[i]) {

  但实际上这两个循环加起来的循环次数一共只有$n$,即总区间的数量。加上每次循环都要用$\log{n}$的计算量去修改线段树和二分,因此时间复杂度为$O(n \log{n})$,常数很大就对了。

 

参考资料

  Codeforces Round #826 (Div. 3) F 线段树 + STL:https://zhuanlan.zhihu.com/p/572800638

posted @ 2022-10-15 19:52  onlyblues  阅读(80)  评论(0编辑  收藏  举报
Web Analytics