Codeforces1741 F. Multi-Colored Segments(离散化/multiset/差分)

传送门


题目大意

在X轴的正半轴上给定 n ( 1 ≤ n ≤ 2 e 5 ) n(1 \leq n \leq 2e5) n(1n2e5) 个线段 l i , r i ( 1 ≤ l , r ≤ 1 e 9 ) l_i,r_i(1 \leq l, r \leq 1e9) li,ri(1l,r1e9),并且每个线段都有一个颜色作为编号 c i ( 1 ≤ c ≤ n ) c_i(1 \leq c \leq n) ci(1cn)。对于每个线段,需要求出距离它最近的一个和它不同颜色的线段,并输出其距离。注意如果两条线段有交点那么距离为0。例如:
在这里插入图片描述

解题思路

考虑线段 a a a,设影响它答案的线段为 b b b,它们只可能出现如下几种情况:

  1. b b b a a a 的右边,且二者无交点,即满足 r a < l b r_a < l_b ra<lb
  2. b b b a a a 的左边,且二者无交点,即满足 r b > l a r_b > l_a rb>la
  3. 二者至少有一个交点

对于第一种情况,若不考虑颜色,只需要对所有线段按照左端点排序,然后二分上界找到最小的 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(1l,r1e9),若 s u m [ r ] − s u m [ l − 1 ] > r − l + 1 sum[r] - sum[l - 1] > r - l + 1 sum[r]sum[l1]>rl+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
The problem statement can be found at Codeforces website. Approach: Let's start by looking at some examples: - 1, 2, 3, 4, 5 → No moves needed. - 2, 1, 3, 5, 4 → One move needed: swap index 1 and 2. - 5, 4, 3, 2, 1 → Two moves needed: swap index 1 and 5, then swap index 2 and 4. We can observe that in order to minimize the number of moves, we need to sort the array in non-descending order and keep track of the number of swaps we make. We can use bubble sort to sort the array and count the number of swaps. Let's see how bubble sort works: - Start from the first element, compare it with the second element, and swap them if the second element is smaller. - Move to the second element, compare it with the third element, and swap them if the third element is smaller. - Continue this process until the second-to-last element. At this point, the largest element is in the last position. - Repeat the above process for the remaining elements, but exclude the last position. In each iteration of the above process, we can count the number of swaps made. Therefore, the total number of swaps needed to sort the array can be obtained by summing up the number of swaps made in each iteration. Implementation: We can implement the above approach using a simple bubble sort algorithm. Here's the code: - First, we read the input array and store it in a vector. - We define a variable to keep track of the total number of swaps made and set it to 0. - We run a loop from the first element to the second-to-last element. - In each iteration of the above loop, we run another loop from the first element to the second-to-last element minus the current iteration index. - In each iteration of the inner loop, we compare the current element with the next element and swap them if the next element is smaller. - If a swap is made, we increment the total number of swaps made. - Finally, we output the total number of swaps made. Time Complexity: The time complexity of bubble sort is O(n^2). Therefore, the overall time complexity of the solution is O(n^2). Space Complexity: We are using a vector to store the input array. Therefore, the space complexity of the solution is O(n). Let's see the implementation of the solution.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值