F. The Treasure of The Segments

题意:

给出n个线段组成的集合,第i个线段用 \(\{l_i, r_i\}\) 表示线段从坐标轴的点\(l_i\)横跨到点\(r_i\)。现在你可以删除其中的一些线段,使得剩下的线段组成的集合中至少存在一个线段满足:这个线段与所有其他线段都相交。现在问你最少需要删除几条边可以得到满足要求的线段集合。

思路:

我们枚举每个线段,让这个线段能够与所有其他线段相交,这时候我们只需要算出不和这个边相交的线段的个数。算这个的方法也很简单,可以想一下什么情况下两个线段 \(\{l_1, r_1\}\) \(\{l_2, r_2\}\) 不相交,也就是 \(l_2 > r1\) 或者 \(r_2 < l_1\) 的情况下,两个边就不会相交。所以我们可以用两个数组来分别存储边的\(l\) 和\(r\),排序之后,就可以用二分来快速找到不与当前枚举的边相交的边的个数。

小注:

之前有看其他博主写的博客,有提到二分比较难写,主要是因为没有善用\(lower\_bound\)和\(upper\_bound\)。

AC代码:

#include <cstdio>
#include <algorithm>

#define pii pair<int, int>
#define fr first
#define sc second

const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;

std::pii a[maxn];
int l[maxn], r[maxn];

int main () {
    int T, n;
    scanf ("%d", &T);
    while (T--) {
        scanf ("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf ("%d %d", &a[i].fr, &a[i].sc);
            l[i] = a[i].fr;
            r[i] = a[i].sc;
        }
        std::sort (l, l + n);
        std::sort (r, r + n);
        int ans = inf;
        for (int i = 0; i < n; i++) {
            int tot = 0;
            tot = 0;
            tot += (int)(std::lower_bound(r, r + n, a[i].fr) - r);
            tot += n - (int)(std::upper_bound(l, l + n, a[i].sc) - l);
            ans = std::min (ans, tot);
        }
        printf ("%d\n", ans);
    }
  	return 0;
}

这题一开始没想到那么简洁的做法,最开始用线段树维护不和当前枚举的线段相交的个数,结果。。。T了。

杀鸡用牛刀超时未AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

#define pii pair<int, int>
#define mp(a, b) make_pair(a, b)
#define fr first
#define sc second

const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;

std::pii in[maxn], num[maxn];
int lisan[maxn << 1], tot_lisan = 1;

class seg_tree {
public:
    int tree[maxn << 2];

    void init() {
        memset (tree, 0, sizeof tree);
    }

    void push_up (int p) {
        tree[p] = tree[p << 1] + tree[p << 1 | 1];
    }

    void update (int p, int l, int r, int idx) {
        if (l == r) {
            tree[p]++;
        } else {
            int mid = (l + r) >> 1;
            if (idx <= mid) {
                update (p << 1, l, mid, idx);
            } else {
                update (p << 1 | 1, mid + 1, r, idx);
            }
            push_up(p);
        }
    }

    int query (int p, int l, int r, int ql, int qr) {
        if (r < ql || l > qr) {
            return 0;
        } else if (l >= ql && r <= qr) {
            return tree[p];
        } else {
            int mid = (l + r) >> 1;
            int ans = 0;
            ans += query (p << 1, l, mid, ql, qr);
            ans += query (p << 1 | 1, mid + 1, r, ql, qr);
            return ans;
        }
    }
}t1, t2;

int main () {
    int T, n;
    scanf ("%d", &T);
    while (T--) {
        t1.init();
        t2.init();
        tot_lisan = 1;
        memset (num , 0, sizeof num);
        scanf ("%d", &n);
        for (int i = 0; i < n; i++) {
            int l, r;
            scanf ("%d %d", &l, &r);
            in[i] = std::mp(l, r);
            lisan[tot_lisan++] = l;
            lisan[tot_lisan++] = r;
        }
        std::sort (lisan, lisan + tot_lisan);
        std::sort (in, in + n);
        tot_lisan = (int)(std::unique(lisan, lisan + tot_lisan) - lisan);
        for (int i = 0; i < n; i++) {
            int pl = (int)(std::lower_bound(lisan, lisan + tot_lisan, in[i].fr) - lisan);
            int pr = (int)(std::lower_bound(lisan, lisan + tot_lisan, in[i].sc) - lisan);
            num[i].fr = pl;
            num[i].sc = pr;
            t1.update (1, 0, tot_lisan, pl);
            t2.update (1, 0, tot_lisan, pr);
        }
        int ans = inf;
        for (int i = 0; i < n; i++) {
            int pl = num[i].fr;
            int pr = num[i].sc;
            int tot = 0;
            tot += t1.query (1, 0, tot_lisan, pr + 1, tot_lisan);
            tot += t2.query (1, 0, tot_lisan, 0, pl - 1);
            ans = std::min (ans, tot);
        }
        printf ("%d\n", ans);
    }
    return 0;
}
上一篇:Codeforces - Special Segments of Permutation


下一篇:shell判断文件是否有变化