码迷,mamicode.com
首页 > 其他好文 > 详细

ACM学习历程——NOJ1113 Game I(贪心 || 线段树)

时间:2015-04-28 20:23:01      阅读:191      评论:0      收藏:0      [点我收藏+]

标签:

 

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\n", 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\n", Query(1, cnt, 1));
    }
    return 0;
}

 

 

ACM学习历程——NOJ1113 Game I(贪心 || 线段树)

标签:

原文地址:http://www.cnblogs.com/andyqsmart/p/4463613.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!