Description
尼克发明了这样一个游戏:在一个坐标轴上,有一些圆,这些圆的圆心都在x轴上,现在给定一个x轴上的点,保证该点没有在这些圆内(以及圆上),尼克可以以这个点为圆心做任意大小的圆,他想知道自己做多可以与多少个给定的圆相交(相切也算,包含不算)。
Input
输入有多组数据 输入到文件尾
每一组数据有一个整数n(1<=n<=100000),表示总共有n个圆。
接下是n行,每行两个整数xi,ri表示该圆的圆心坐标和半径。
接下来一行为一个整数x,表示尼克选取点的位置。
x xi的范围[-10^9,10^9] ri的范围[1,10^9]
总共有最多10组数据。
Output
每组数据输出一行,表示尼克最多可以覆盖多少个圆。
Sample Input
2 1 2 2 1 4
Sample Output
2
这个题目条件转换一下就是满足|r-d| <= R <= r+d的R就能与r半径的圆相交,其中d是两圆圆心的距离。
这样就变成了区间增值,然后查询区间中的最大值。
首先想到的是线段树,复杂度是O(2n*log(2n))。不过由于半径范围的值是离散的,所以采用map进行映射,使其连续。不过AC用时500ms左右。
然后发现其实直接处理后直接贪心就行。将所有区间的左右端点排序,排序时需要保存标记,用于记录这个端点是某个区间的左端点还是右端点。然后就是扫一遍,对于是左端点的自然值加一,对于右端点的自然值减一,然后贪心过程中的最大值。AC用时85ms左右。
贪心代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <map> #include <set> #include <algorithm> #define LL long long using namespace std; struct node { LL index; bool isleft; }ind[200005]; int n, ans; LL x[100005], r[100005], xx; bool cmp(node a, node b) { return a.index < b.index; } LL Abs(LL aa) { if (aa < 0) return -aa; else return aa; } void Init() { LL d, Left, Right; for (int i = 0; i < n; ++i) { d = Abs(x[i]-xx); Left = Abs(r[i]-d); Right = r[i]+d; ind[i<<1].index = Left; ind[i<<1].isleft = true; ind[i<<1|1].index = Right; ind[i<<1|1].isleft = false; } sort(ind, ind+2*n, cmp); } int main() { //freopen("test.in", "r", stdin); while (scanf("%d", &n) != EOF) { for (int i = 0; i < n; ++i) { scanf("%lld%lld", &x[i], &r[i]); } scanf("%lld", &xx); Init(); int len = 2*n; int now = 0; ans = 0; for (int i = 0; i < len; ++i) { if (ind[i].isleft) now++; else now--; ans = max(ans, now); } printf("%d ", ans); } return 0; }
线段树代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <map> #include <set> #include <algorithm> #define LL long long using namespace std; //线段树 //区间每点增值,求区间最值 const int maxn = 100005; struct node { int lt, rt; int val, add; }tree[8*maxn]; //向下更新 void PushDown(int id) { if (tree[id].add != 0) { tree[id<<1].add += tree[id].add; tree[id<<1].val += tree[id].add; tree[id<<1|1].add += tree[id].add; tree[id<<1|1].val += tree[id].add; tree[id].add = 0; } } //向上更新 void PushUp(int id) { tree[id].val = max(tree[id<<1].val, tree[id<<1|1].val); } //建立线段树 void Build(int lt, int rt, int id) { tree[id].lt = lt; tree[id].rt = rt; tree[id].val = 0;//每段的初值,根据题目要求 tree[id].add = 0; if (lt == rt) { //tree[id].add = ??; return; } int mid = (lt + rt) >> 1; Build(lt, mid, id<<1); Build(mid+1, rt, id<<1|1); //PushUp(id); } //增加区间内每个点固定的值 void Add(int lt, int rt, int id, int pls) { if (lt <= tree[id].lt && rt >= tree[id].rt) { tree[id].add += pls; tree[id].val += pls; return; } PushDown(id); int mid = (tree[id].lt + tree[id].rt) >> 1; if (lt <= mid) Add(lt, rt, id<<1, pls); if (rt > mid) Add(lt, rt, id<<1|1, pls); PushUp(id); } //查询某段区间内的zuizhi int Query(int lt, int rt, int id) { if (lt <= tree[id].lt && rt >= tree[id].rt) return tree[id].val; PushDown(id); int mid = (tree[id].lt + tree[id].rt) >> 1; if (rt <= mid) return Query(lt, rt, id<<1); if (lt > mid) return Query(lt, rt, id<<1|1); return max(Query(lt, rt, id<<1), Query(lt, rt, id<<1|1)); } int n, cnt; LL x[100005], r[100005], ind[200005], xx; LL Left[100005], Right[100005]; map <LL, int> id; bool cmp(LL a, LL b) { return a < b; } LL Abs(LL aa) { if (aa < 0) return -aa; else return aa; } void Init() { id.clear(); LL d; for (int i = 0; i < n; ++i) { d = Abs(x[i]-xx); Left[i] = Abs(r[i]-d); Right[i] = r[i]+d; ind[i<<1] = Left[i]; ind[i<<1|1] = Right[i]; } sort(ind, ind+2*n, cmp); int len = 2*n; cnt = 1; for (int i = 0; i < len; ++i) { if (i == 0) { id[ind[0]] = 1; continue; } if (ind[i] != ind[i-1]) { id[ind[i]] = ++cnt; } } Build(1, cnt, 1); } int main() { //freopen("test.in", "r", stdin); while (scanf("%d", &n) != EOF) { for (int i = 0; i < n; ++i) { scanf("%lld%lld", &x[i], &r[i]); } scanf("%lld", &xx); Init(); for (int i = 0; i < n; ++i) { Add(id[Left[i]], id[Right[i]], 1, 1); } printf("%d ", Query(1, cnt, 1)); } return 0; }