其他分享
首页 > 其他分享> > Codeforces Round #690 (Div. 3) F. The Treasure of The Segments (二分+主席树)

Codeforces Round #690 (Div. 3) F. The Treasure of The Segments (二分+主席树)

作者:互联网

题目链接

题意:定义good线段集合:该集合中至少存在一条线段(x)与集合内其他所有线段(others)都至少有一个交点。给定n条线段,求最少删除几条线段,使得剩下的线段集合是good。

思路:稍微转化一下题意,就是求good线段集合的大小最大是多少。考虑枚举每一个线段,让其作为线段X,然后求出其他线段中与X有交点的个数。先将线段按照左端点排序。对于当前枚举的线段X,[L,R],在数组下标[X,N]的范围内二分找到最大的左端点小于等于R的位置pos。那么[X+1,pos]区间内的线段都与X至少存在一个 交点,都可以加入到good集合中。目前已经找到了所有左端点与线段X有交点的线段,还少算了那些左端点不与线段X相交,但右端点与线段X相交的线段,这部分也可以加入good集合。然而右端点是无序的,也就不能二分,得另寻他法。左端点不与线段X相交的线段集合就是[1,X-1],在这里面找到右端点与线段X有交点的。这里考虑以线段右端点作为权值,建立主席树(需要先离散化),区间查询权值介于【L,R】之间的个数。二分算出的部分加上主席树查询得到的部分相加就是其他线段中与X有交点的个数。总体时间复杂度 n ∗ l o g n n*log^n n∗logn

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
int a[MAXN];
vector<int> v;
int getid(int x)
{
    return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
struct node
{
    int l, r;
    int sum;
    int ans;
} tree[MAXN * 20];
int root[MAXN];
int cnt = 0;
#define ls(x) tree[x].l
#define rs(x) tree[x].r
void insert(int l, int r, int pre, int &now, int val)
{

    now = ++cnt;
    tree[now] = tree[pre];
    tree[now].sum++;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    if (val <= mid)
        insert(l, mid, ls(pre), ls(now), val);
    if (val > mid)
        insert(mid + 1, r, rs(pre), rs(now), val);
}
int query(int l, int r, int qL, int qR, int rootL, int rootR)
{
    if (l >= qL && r <= qR)
    {
        return tree[rootR].sum - tree[rootL].sum;
    }
    int mid = (l + r) >> 1;
    int res = 0;
    if (qL <= mid)
        res += query(l, mid, qL, qR, ls(rootL), ls(rootR));
    if (qR > mid)
        res += query(mid + 1, r, qL, qR, rs(rootL), rs(rootR));
    return res;
}
struct Line
{
    int l, r;
} L[MAXN];

int main()
{
#ifdef DEBUG
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
#endif
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int n;
        scanf("%d", &n);
        v.clear();
        for (int i = 1; i <= n; i++)
        {
            root[i]=0;
            scanf("%d%d", &L[i].l, &L[i].r);
            v.push_back(L[i].l);
            v.push_back(L[i].r);
        }
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        sort(L + 1, L + 1 + n, [](const Line &fi, const Line &se) {
            return fi.l < se.l;
        });
        for (int i = 1; i <= n; i++)
        {
            insert(1, v.size(), root[i - 1], root[i], getid(L[i].r));
        }
        int res = 0;
        for (int i = 1; i <= n; i++)
        {
            int l = i, r = n;
            while (l <= r)
            {
                int mid = l + r >> 1;
                if (L[mid].l <= L[i].r)
                {
                    l = mid + 1;
                }
                else
                    r = mid - 1;
            }
            int ans = l - i;
            //[1,i-1] [L[i].l,L[i].r]
            ans += query(1, v.size(), getid(L[i].l), getid(L[i].r),root[0],root[i-1]);
            res = max(res, ans);
        }
        printf("%d\n", n - res);
        //memset(root, 0, sizeof(root));
        //memset(tree, 0, sizeof(tree));
        for(int i=0;i<=cnt;i++)tree[i]=tree[0];
        cnt = 0;
    }
    return 0;
}

标签:690,int,线段,Treasure,tree,mid,端点,root,Segments
来源: https://blog.csdn.net/weixin_43645523/article/details/111257349