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