JZOJ 5456. 【NOIP2017提高A组冲刺11.6】奇怪的队列

25 篇文章 0 订阅
8 篇文章 1 订阅

Description

nodgd的粉丝太多了,每天都会有很多人排队要签名。
今天有n个人排队,每个人的身高都是一个整数,且互不相同。很不巧,nodgd今天去忙别的事情去了,就只好让这些粉丝们明天再来。同时nodgd提出了一个要求,每个人都要记住自己前面与多少个比自己高的人,以便于明天恢复到今天的顺序。
但是,粉丝们或多或少都是有些失望的,失望使她们晕头转向、神魂颠倒,已经分不清楚哪一边是“前面”了,于是她们可能是记住了前面比自己高的人的个数,也可能是记住了后面比自己高的人的个数,而且他们不知道自己记住的是哪一个方向。
nodgd觉得,即使这样明天也能恢复出一个排队顺序,使得任意一个人的两个方向中至少有一个方向上的比他高的人数和他记住的数字相同。可惜n比较大,显然需要写个程序来解决,nodgd很忙,写程序这种事情就交给你了。

Input

第一行输入一个整数n,表示指令的条数。
接下来n行,每行两个整数ai,bi,表示一个人的身高和她记住的数字,保证身高互不相同。

Output

输出一行,这个队列里从前到后的每个人的身高。如果有多个答案满足题意,输出字典序最小。如果不存在满足题意的排列,输出“impossible”(不含引号)。

Sample Input

输入1:

4
4 1
3 1
6 0
2 0

输入2:

6
1 5
8 0
3 1
4 0
2 0
6 0

Sample Output

输出1:

2 4 3 6

输出2:

1 2 4 3 6 8

Data Constraint

对于 40% 的数据: N10
对于 60% 的数据: N1000
对于 100% 的数据: N100000

Hint

【样例解释1】
在所给出的答案队列中,第一个人身高为2,前面有0个人比他高,所以他是输入的第4个人;第二个人身高为4,右边有1个人比他高,所以他是输入的第1个人;第三个人身高为3,右边有1个人比他高,所以他是输入的第2个人;第四个人身高为6,左边有0个人比他高,所以他是输入的第3个人。
显然,如果排列为“6 3 4 2”也是满足题意的,但是字典序不是最小的。

Solution

  • 我们可以按高度从大到小排一遍序,倒着处理。

  • 设当前做到第 i 个人,前 i1 个人(排好序了)到已经站好了。

  • 我们操作时保证满足条件,为了满足字典序最小,那么填的位置 k 肯定是满足:

    k=Min{bi+1,i1bi}

  • 即在往后看和往前看中选一个较小的填进去,最后输出排好的序列即可。

  • 其中的插入操作可以用线段树的二分查找、或者Splay维护。

  • 我用的是 Splay ,时间复杂度为 O(N log N) ,代码如下:

Code

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+3,inf=1e9;
struct data
{
    int x,y;
}a[N];
int root,tot;
int fa[N],size[N],key[N],s[N][2];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline bool cmp(data x,data y)
{
    return x.x>y.x;
}
inline bool pd(int x)
{
    return x==s[fa[x]][1];
}
inline void update(int x)
{
    size[x]=size[s[x][0]]+size[s[x][1]]+1;
}
inline void newnode(int &v,int val,int p)
{
    v=++tot;
    fa[v]=p,size[v]=1,key[v]=val;
    s[v][0]=s[v][1]=0;
}
inline void rotate(int x)
{
    int y=fa[x],w=pd(x);
    if(s[y][w]=s[x][w^1]) fa[s[x][w^1]]=y;
    if(fa[x]=fa[y]) s[fa[y]][pd(y)]=x;
    s[fa[y]=x][w^1]=y;
    update(y);
}
inline void splay(int x,int k)
{
    for(int y;(y=fa[x])^k;rotate(x))
        if(fa[y]^k) rotate(pd(x)==pd(y)?y:x);
    update(x);
    if(!k) root=x;
}
inline int kth(int x,int k)
{
    if(size[s[x][0]]+1==k) return x;
    if(k<=size[s[x][0]]) return kth(s[x][0],k);
    return kth(s[x][1],k-size[s[x][0]]-1);
}
inline void print(int x)
{
    if(s[x][0]) print(s[x][0]);
    if(key[x]<inf) write(key[x]),putchar(' ');
    if(s[x][1]) print(s[x][1]);
}
int main()
{
    int n=read();
    for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
        if(a[i].y>i-1) return 0&puts("impossible");
    newnode(root,inf,0);
    newnode(s[root][1],inf,root);
    update(root);
    for(int i=1;i<=n;i++)
    {
        int k=min(a[i].y,(size[root]-2)-a[i].y)+1;
        splay(kth(root,k),0);
        splay(kth(root,k+1),root);
        newnode(s[s[root][1]][0],a[i].x,s[root][1]);
        update(s[root][1]),update(root);
    }
    print(root);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值