牛客小白月赛16 F-小石的妹子(线段树)

链接:https://ac.nowcoder.com/acm/contest/949/F
来源:牛客网

题目描述

小石有 n 个妹子,每个妹子都有一个细心程度 ai 和一个热心程度 bi
小石想给她们一个重要程度 ti(重要程度为 1 表示最重要,重要程度越小表示越重要)。如果一个妹子 i 的细心程度和热心程度都比妹子 j 大,那么妹子 i 的重要程度要大于妹子 j 的重要程度,即妹子 i 比妹子 j 重要。
流程如下:
每次从所有没有重要程度的妹子中,找到若干妹子。对于这些妹子的任意一个,需要保证没有其他妹子比她更重要。然后把她们的重要程度标为 1 。下一次再从剩下没有重要程度的妹子中找到若干妹子,依然符合上述条件,然后把她们的重要程度标为 2,……,重复直到所有妹子都有自己的重要程度。
由于妹子太多,小石忙不过来,请你帮帮他。

输入描述:

第一行输入一个正整数 n,表示妹子的数量。
接下来 n 行,每行两个正整数 ai,bi,描述每个妹子的细心程度和热心程度。
保证所有的 ai 两两不等,所有的bi 两两不等。

输出描述:

共 n 行,第 i 行输出一个正整数 ti 表示第 i 个妹子的重要程度。

示例1
输入

5
1 4
2 2
3 3
4 1
5 5

输出

2
3
2
2
1

说明

第一轮取第 5 个妹子(5 5),因为没有其他妹子比她重要,标记为 1;

第二轮取编号为 1,3,4 的妹子,因为对于其中的任意一个妹子,都没有其他妹子比她们重要,标记为 2;

第三轮把编号为 2 的妹子标记为 3 。

备注:

1 ≤ n ≤ 105, 1 ≤ ai,bi ≤ 109

分析:

先按a从大到小排序,然后以此顺序进行排名,对于第 i 个女生,rk[i] = max{ rk[j] } + 1( b[j] > b[i] )

其中 max{ rk[j] }现有其b[j] 比 b[i] 大的女生最大的那个排名(即现有的最低排名),又因为是按a从大到小进行排名,所以max{ rk[j] } 里面的非0值都是a、b均比 a[i]、b[i]大的女生的排名,故要找最大的那个排名。

所以可以用线段树维护max,b[i]作为下标,rk[i]作为值,初始化线段树都为0,然后每次把新的排名往里面加 (单点更新)。但要注意先将b离散化处理一下。



以下代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const int maxn=1e5+50;
int n;
struct node
{
    int id;
    int a;
    int b;
}x[maxn];
bool cmp(const node &x,const node &y){ return x.a>y.a; }
void Hash()
{
    int B[maxn];
    for(int i=1;i<=n;i++)
        B[i]=x[i].b;
    sort(B+1,B+n+1);
    for(int i=1;i<=n;i++)    //无相同数据,无需unique()去重
        x[i].b=lower_bound(B+1,B+n+1,x[i].b)-B;
}
int t[4*maxn];
void updata(int rt,int l,int r,int k,int num)   //单点更新
{
    if(l==r)
    {
        t[rt]=num;
        return;
    }
    int mid=(l+r)>>1;
    if(k<=mid)
        updata(rt<<1,l,mid,k,num);
    else
        updata(rt<<1|1,mid+1,r,k,num);
    t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
int query(int rt,int l,int r,int ql,int qr)      //区间查询
{
    if(ql<=l&&r<=qr)
        return t[rt];
    if(r<ql||l>qr)
        return 0;
    int mid=(l+r)>>1;
    return max(query(rt<<1,l,mid,ql,qr),query(rt<<1|1,mid+1,r,ql,qr));
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&x[i].a,&x[i].b);
        x[i].id=i;
    }
    sort(x+1,x+n+1,cmp);    //按a进行排序
    Hash();      //离散化处理b
    int rk[maxn];
    memset(t,0,sizeof(t));  //线段树初始化为0
    for(int i=1;i<=n;i++)
    {
        rk[x[i].id]=query(1,1,n,x[i].b,n)+1;    //在比b[i]大的里面(b[i]~n)找最大的rk
        updata(1,1,n,x[i].b,rk[x[i].id]);       //更新最大值
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",rk[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值