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 li, ri and ci (1liri109,1cin), where li and ri are are the coordinates of the ends of the i-th segment, and ci 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 (1t104) — 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 (2n2105) — the number of segments.

The next n lines contain descriptions of the segments. Each segment is described by three integers li, ri and ci (1liri109,1cin) — 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 2105.

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]如果有srsl1>rl+1,说明至少存在一个区间与当前区间有交集。这是一种解决方案,同时还可以用值域线段树(需要离散化),原理也是一样的。因为后面会涉及到区间的修改,因此这里用线段树来解决这个问题。

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

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

  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,即总区间的数量。加上每次循环都要用logn的计算量去修改线段树和二分,因此时间复杂度为O(nlogn),常数很大就对了。

 

参考资料

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

posted @   onlyblues  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示