HDU 5493 线段树

点击打开链接

题意:给n个人的身高和一个k,k代表的是这个人前边比他高的人的数量或者是后面的比他高的人的数量,问能否将这n个人满足条件的排序,有多解时输出字典序最小的

思路:看到100000个人,感觉是线段树,但是没什么思路,看了大神们的博客,发现只要转化一下便是简单的线段树模型了,对于每个人来说,k就是前边留k个位置,后者后边留k个位置,当然这两个我肯定要选的是小的那个位置,这要是为了满足字典序最小,而这样做的前提是先将n个人按高度排序,为什么这么排,我们可以这样想,对于高的人来说,矮个的人对他的位置肯定是没有影响的,因为他的k是比他高的人产生的,这样我们可以维护一个线段树,如现在高度的人要在第pos的位置,那么我们找到线段树中的第pos个不为0的位置更新就好了,刚刚写的二分+线段树找位置写的超时了,换成树状数组应该可以过,没尝试,也可以直接更新的时候就找到位置,这样更简单一些

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int INF=0x3f3f3f3f3f3f3f3f;
const int maxn=100010;
int num[maxn*4],ans[maxn];
struct edge{
    int height,k;
}es[maxn];
bool cmp(const edge &a,const edge &b){
    return a.height<b.height;
}
void buildtree(int le,int ri,int node){
    if(le==ri){
        num[node]=1;
        return ;
    }
    int t=(le+ri)>>1;
    buildtree(le,t,node<<1);
    buildtree(t+1,ri,node<<1|1);
    num[node]=num[node<<1]+num[node<<1|1];
}
void update(int pos,int k,int le,int ri,int node){
    if(le==ri){
        num[node]=0;
        ans[le]=es[k].height;
        return ;
    }
    int t=(le+ri)>>1;
    if(pos<=num[node<<1]) update(pos,k,le,t,node<<1);
    else update(pos-num[node<<1],k,t+1,ri,node<<1|1);
    num[node]=num[node<<1]+num[node<<1|1];
}
int main(){
    int T,cas=1,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&es[i].height,&es[i].k);
        sort(es+1,es+1+n,cmp);
        int flag=0,sum=n;
        buildtree(1,n,1);
        for(int i=1;i<=n;i++){
            if(sum-es[i].k<=0){
                flag=1;break;
            }
            int pos1=sum-es[i].k;
            int pos2=++es[i].k;sum--;
            int id=min(pos1,pos2);
            update(id,i,1,n,1);
        }
        printf("Case #%d: ",cas++);
        if(flag) printf("impossible\n");
        else{
            for(int i=1;i<=n;i++){
                if(i!=n) printf("%d ",ans[i]);
                else printf("%d\n",ans[i]);
            }
        }
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值