HDU 5493 Queue(2015 ACM/ICPC Asia Regional Hefei Online )

本文解析了 HDU 5493 的解题思路,通过分析得出只有当 N<i+K//i 时才无解。采用贪心策略,确保较矮的人尽可能站前面,并使用线段树维护区间空位数,实现 O(NlogN) 复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】http://acm.hdu.edu.cn/showproblem.php?pid=5493

【解题报告】
比赛的时候并没有意识到这道水题的本质TAT,一度在想是不是要用2-SAT做,真是too young too simple.
这道题目有一个关键点就是,什么样的数据会impossible呢?

当且仅当N<i+K//i为一个人在n个人中的位置(预先排序),k为比他高的人数

为什么其他情况一定有解呢?假设有从低到高的一个排列,那么我们从高到低开始调整每个人在队列中的位置,将第i个人插在比他高的N-i个人之中即可。

那么我们只需要保证插完之后得到的序列字典序最小。如何保证这个输出方案呢?贪心的想,我们需要使低个尽可能站在前面,所以从低到高的第i个人,他可以让他的左边有k个高个,或者左边有N-i-k个高个,那么我们应当让他左边有尽可能少的高个(这样他才能站在前面)。所以应该取
num=min( k, N-i-k ),表示i左边有num个高个。

到了这里只剩下一个问题,就是如何插入,这一步可以用线段树来维护每个区间里还剩的空位的个数,每一步插入操作复杂度为O(logN),总时间复杂度就是O(NlogN)。

【参考代码】

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

struct node{
    int h,k;
}P[100000+10];

int cmp( node A, node B ){ return A.h<B.h; }

int N;
int tree[100000*4];
int pos[100000+10];

void build( int o, int l, int r )
{
    if(l==r) { tree[o]=1; return; }
    int mid=l+(r-l)/2;
    build(o*2, l, mid );
    build(o*2+1, mid+1, r );
    tree[o]=tree[o*2]+tree[o*2+1];
}

void update( int num, int k, int o, int l, int r )
{
    if( l==r )
    {
        pos[l]=k;
        tree[o]=0;
        return;
    }
    int mid=l+(r-l)/2;
    if( num<tree[o*2] )update( num, k, o*2, l, mid );
    else update( num-tree[o*2], k, o*2+1, mid+1, r );
    tree[o]=tree[o*2]+tree[o*2+1];
}

int main()
{
    int T,kase=0;
    cin>>T;
    while( T-- )
    {
          memset( tree, 0, sizeof(tree) );
        scanf("%d",&N);
        for( int i=1; i<=N; i++ )scanf("%d%d",&P[i].h,&P[i].k);
        sort( P+1, P+1+N, cmp );
        build( 1, 1, N );
        bool ok=true;
        for(  int i=1; i<=N; i++ )
        {
            if( N-i-P[i].k<0 ){ ok=false; break; }
            int num=min( P[i].k, N-i-P[i].k );
            update( num, i, 1, 1, N );
        }
        if(!ok){ printf("Case #%d: impossible\n", ++kase); continue; }
        printf("Case #%d: ",++kase);
        for( int i=1; i<=N; i++ )
        {
            int k=pos[i];
            if(i==N)printf("%d\n",P[k].h);
            else printf("%d ",P[k].h);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值