hdu 4585 Shaolin 线段树

题意:

进入少林寺,在闯关成功后需要找一个已经进入少林寺的僧人进行一场比武。

规则如下:

根据新人的武力值,新人会找一个与他武力值最相近的僧人进行比武。如果有两个符合条件的,他会选择武力值比他小的僧人进行比武。

现在把进入少林寺的人的顺序给你,让你把比武的双方的id输出。

*每个人的武力值以及id都是唯一的。

思路:

线段树

题目中已经把新人入门的顺序给你。只要询问一下新人的左边最大武力值是谁,新人的右边最小武力值是谁即可。

询问完后把新人插入到线段树中即可。

复杂度O(nlogn)。

我这里根据武力值大小离散化了一下。

code:

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <map>
#include <vector>
using namespace std;

const int MAXN = 1e5+5;
const int INF = 0x3f3f3f3f;
const int MINF = -1;

#define start 1, n+1, 1
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1

int n;
map <int, int> mp;

struct ST
{
    int minv, maxv;
}st[MAXN<<2];
struct Monk
{
    int id, gra;
    int rank;
    int id2;        //record the grade level;
}mk[MAXN];

bool cmp(const Monk &a, const Monk &b)
{
    return a.gra < b.gra;
}

bool cmp2(const Monk &a, const Monk &b)
{
    return a.rank < b.rank;
}

void lishanhua()
{
    int id = 1;
    for(int i = 0;i < n+1; i++)
        mk[i].id2 = id++;
}

void update(int l, int r, int rt, int pp, int val)
{
    if(l == r)
    {
        st[rt].minv = min(st[rt].minv, val);
        st[rt].maxv = max(st[rt].maxv, val);
        return ;
    }

    int mid = (l+r)/2;
    if(pp <= mid)   update(lson, pp, val);
    else            update(rson, pp, val);

    st[rt].minv = min(st[rt<<1].minv, st[rt<<1|1].minv);
    st[rt].maxv = max(st[rt<<1].maxv, st[rt<<1|1].maxv);
}

int querymin(int l, int r, int rt, int x, int y)
{
    if(x <= l && y >= r)
    {
        return st[rt].minv;
    }

    int mid = (l+r)/2;
    int tt = INF;
    if(x <= mid) tt = min(tt, querymin(lson, x, y));
    if(y > mid) tt = min(tt, querymin(rson, x, y));

    return tt;
}

int querymax(int l, int r, int rt, int x, int y)
{
    if(x <= l && y >= r)
    {
        return st[rt].maxv;
    }

    int mid = (l+r)/2;
    int tt = -1;
    if(x <= mid) tt = max(tt, querymax(lson, x, y));
    if(y > mid) tt = max(tt, querymax(rson, x, y));
    
    return tt;
}
        

int main()
{
    while(scanf("%d", &n) != EOF)
    {
        if(n == 0) break;
        
        mk[0] = (Monk){1, 1e9, 0};
        for(int i = 1;i <= n; i++)
        {
            scanf("%d%d", &mk[i].id, &mk[i].gra);
            mk[i].rank = i;
        }
        
        //init seg tree
        for(int i = 1;i <= (n<<2); i++)
        {
            st[i].minv = INF, st[i].maxv = -1;
        }

        sort(mk, mk+n+1, cmp);
        lishanhua();
        sort(mk, mk+n+1, cmp2);

        mp.clear();
        for(int i = 0;i < n+1; i++)
        {
            mp[mk[i].gra] = mk[i].id;
        }

        update(start, mk[0].id2, mk[0].gra);
        for(int i = 1;i <= n; i++)
        {
            const int tt = mk[i].id2;
            int minv = INF, maxv = -1;
            minv = min(minv, querymin(start, tt, n+1));
            maxv = max(maxv, querymax(start, 1, tt));
            //cout<<"minv = "<<minv<<" maxv = "<<maxv<<endl;
            
            if(maxv == -1)
                printf("%d %d\n", mk[i].id, mp[minv]);
            else 
            {
                int t1 = abs(minv- mk[i].gra), t2 = abs(maxv - mk[i].gra);
                if(t2 <= t1)
                    printf("%d %d\n", mk[i].id, mp[maxv]);
                else
                    printf("%d %d\n", mk[i].id, mp[minv]);
            }

            update(start, mk[i].id2, mk[i].gra);
        }
    }
    return 0;
}

/*
 5
2 1
3 3
4 2
5 100
6 80
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值