线段树HDU 5493

 

题意转:

1:

题意:给你n个人的身高和他前面或者后面身高大于他的人的个数,求一个字典序最小的满足此条件的序列,如果不存在输出“impossible”。

分析:我们从字典序最小入手,如果一个位置可以放多个数,那么肯定放最小的,所以我们从小到大处理,并且小的数不影响大的数的放置,因为第一个值是比当前人身高大的人的个数。那么我们考虑当前数(保证处理的数是未处理集合中最小的那个),可以存放的位置,并将他放置在最前面以保证字典序最小,这样依次处理完n个数最后用一个Max大小的数组记录每个人的位置即可。

比如这组样例:(为了方便说明我按升序输入)

8

1 0

2 3

3 1

4 2

5 3

6 2

7 1

8 0

用一个数组存储位置,初始时为_ _ _ _ _ _ _ _

第一次选择最小的存放,为1,因为1的权值为0,所以我们不用给1留出空位,保证字典序最小那么放在

第一个位置,此时结果为1 _ _ _ _ _ _ _

第二次选择2,权值为3,那么就要保证2的左边或者右边有3个空位存放比它大的,保证字典序,

此时结果为1 _ _ _ 2 _ _ _ 

第三次选择3,权值为1,同上,此时结果为1 _ 3 _ 2 _ _ _ 

第四次选择4,权值为2,同上,结果为1 _ 3 _ 2 4 _ _

第五次5,结果为1 5 3 _ 2 4 _ _

第六次6,结果为1 5 3 6 2 4 _ _

第七次第八次...

最终结果为 1 5 3 6 2 4 7 8 

2:

从高到低放或者从低到高放都可以,由于本题要输出字典序最小的序列,所以我们使用从低到高放 
由于从低到高放,之后放进去的是都是比当前高的 
考虑当前当前放进去的第i个人,要满足后来的(即比他高的)有恰好k个在他前面或者在他后面 
①:考虑有后来的k个人在他前面就是前面预留出k个位置 
②:考虑有后来的k个人在他后面,已经放了i−1个人,剩下有n−(i−1)个人,再放k个在后面,还有n−(i−1)−k个,

再去掉自己 ,那么问题就转化成了前面预留n−(i−1)−k−1=>n−i−k个 
由于要字典序最小的,小的尽量往前放,所以取个min(k,n−i−k),如果这个值为负了,那么就表示不能放,就是impossible了 
接下来就是普通的线段树求第min(k,n−i−k)+1个位置放进去当前这个人,维护区间空位的个数,查询的时候同时维护一下ans数组就好了
 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <numeric>
#include <functional>
#define RI(N) scanf("%d",&(N))
#define RII(N,M) scanf("%d %d",&(N),&(M))
#define RIII(N,M,K) scanf("%d %d %d",&(N),&(M),&(K))
#define mem(a) memset((a),0,sizeof(a))
using namespace std;
const int inf=1e9;
const int inf1=-1*1e9;
double EPS=1e-10;
typedef long long LL;

int ans[100005];
int tre[100005<<2];
struct P
{
    int val;
    int poi;
};

bool cmp(P p1,P p2)
{
    return p1.val<p2.val;
}
void pushup(int rt)
{
    tre[rt]=tre[rt<<1]+tre[rt<<1|1];
}

void build(int l,int r,int rt)
{
    if(l==r)
    {
        tre[rt]=1;
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int rt,int poi,int add)
{
    if(l==r)
    {
        ans[l]=add;
        tre[rt]=0;
        return ;
    }
    int m=(l+r)>>1;
    if(poi<=tre[rt<<1]) update(l,m,rt<<1,poi,add);
    else update(m+1,r,rt<<1|1,poi-tre[2*rt],add);
    pushup(rt);
}

P p[100005];

int main()
{
    int T,cas=1;
    RI(T);
    while(T--)
    {
        int n;
        RI(n);
        for(int i=0;i<n;i++)
        {
            RII(p[i].val,p[i].poi);
        }
        sort(p,p+n,cmp);
        bool mark=true;
        build(1,n,1);
       // cout<<tre[1]<<" "<<tre[2]<<" "<<tre[3]<<endl;
        for(int i=0;i<n;i++)
        {
            int t=p[i].poi;
            int rem=n-i;
            if(t>=rem)
            {
                mark=false;
                break;
            }
            if(n-i-t>t+1) {
                    update(1,n,1,t+1,p[i].val);
                //cout<<"t+1 = "<<t+1<<endl;
            }
            else {
                   // cout<<"n-i-t+1 = "<<n-i-t+1<<endl;
                    update(1,n,1,n-i-t,p[i].val);
            }
        }
        if(mark)
        {
            printf("Case #%d:",cas++);
            for(int i=1;i<=n;i++)
                printf(" %d",ans[i]);
            printf("\n");

        }
        else printf("Case #%d: impossible\n",cas++);

    }

    return 0;
}

 

解题思路:

题目要求输出身高字典序最小的情况,那么先对身高按从小到大排序。然后按身高从小

到大确定每个人的位置。由于每次都是从小到大,则每次放进的人都是比之前的人高的。

设当前放进的人是身高从小到大第 i 个人,如果要满足他前边或后边刚好有 k 个人比他

高,则:

如果前边有 k 个人比他高,则现在前边要空出 k 个位置来放置比他高的人,他就放置在

第 k+1 的位置上。

如果后边有 k 个人比他高,已知已经放置了 i-1 个人,除了他自己,还剩下 N - (i-1) - 1

个人。而后边还要放 k 个比他高的人,则前边还应该要空出 N - (i-1) - 1 - k 个位置,则

他自己放置在第 N - i - k + 1 的位置上。

为了使身高的字典序最小,则每次都应该尽可能的靠前。所以应该选择这两种情况中的最

小情况,即 min(k,N - i - k)。

现在考虑不可能的情况。第一种在前边要空出 k 个比他高的位置,已经算上自己共有 i+k

个人,如果这个数应该小于等于 N 否则不能放置( i + k > N,则不能放置)。第二种在前边

空出 N - i - k 个位置,则 N - i - k > 0(同样是i + k > N,则不能放置)。

综上可得:如果对于当前要放的第 i个人,如果  i + k > N,则无解,输出"impossible"。
 

#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
const int maxn = 222222;
int sum[maxn<<2];
struct node
{
    int h, num;
} a[maxn<<2];
int val[maxn<<2];
bool cmp(node a, node b)
{
    return a.h < b.h;
}
int MIN(int x,int y)
{
    if(x > y)
        return y;
    return x;
}
void PushUP(int rt) //把当前结点的信息更新到父结点
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
 
void build(int l,int r,int rt)
{
    if(l == r)
    {
        sum[rt] = 1;
        //scanf("%d",&sum[rt]);
        return ;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt);
}
void update(int p,int sc,int l,int r,int rt)
{
    if(l == r) //叶节点
    {
        val[l] = sc;
        sum[rt] = 0;
        return ;
    }
    int mid = (l + r) >> 1;
    if(p <= sum[rt*2])//注意,debug
        update(p , sc , lson);
    else
        update(p-sum[rt*2] , sc , rson);
    PushUP(rt);
}
 
int main()
{
    int t;
    int N, M;
    int cas = 0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&N);
        for(int i = 1; i <= N; i++)
        {
            scanf("%d%d",&a[i].h,&a[i].num);
        }
        sort(a+1,a+N+1,cmp);
        build(1, N, 1); //建树
        int flag = 0;
        for(int i = 1; i <= N; i++)
        {
            int cnt = N-i;//剩余的空格数
            int tt = cnt-a[i].num;
            tt = MIN(tt, a[i].num);
            if(tt < 0)//不能放了
            {
                flag = 1;
                break;
            }
            update(tt+1, a[i].h, 1, N, 1);
        }
 
        printf("Case #%d: ",++cas);
        if(flag)
            printf("impossible\n");
        else
        {
            for(int i = 1; i <= N; i++)
            {
                if(i == 1)
                    printf("%d",val[i]);
                else
                    printf(" %d",val[i]);
            }
            printf("\n");
        }
    }
    return 0;
}
/*
99
8
1 0
2 3
3 1
4 2
5 3
6 2
7 1
8 0
*/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值