题目大意:
题目链接:https://jzoj.net/senior/#main/show/5456
题目图片:
http://wx1.sinaimg.cn/mw690/0060lm7Tly1fwerg3tpiyj30jf0bigm3.jpg
http://wx2.sinaimg.cn/mw690/0060lm7Tly1fwerg3u33aj30k40gz3yu.jpg
给出个人的身高,第个人记得他的前面或者后面有个人比他高。这些人字典序最小的排列。
思路:
40分做法:
先将所有人的身高排序,然后全排列,再判断是否成立。
时间复杂度:
代码
100分做法:
按照样例一来模拟
4
4 1
3 1
6 0
2 0
先按身高排序(从小到大)
4
2 0
3 1
4 1
6 0
首先,我们有一个长度为4的空串
身高为2,前(后)面有0个人比他高的人。那么很明显,就只能插入第一个或最后一个位置。
为了字典序最小,那么就将这个人插入到第一个位置。
然后看下一个人,身高为3,前(后)面有一个人比他高。
那么可能的位置也就只有一个,就插入三号位。
身高为4,前(后)面没有人比他高。
那么剩余两个位置都有可能。
选择字典序更小的。
那么最后一个人就只能放在最后一个位置了。
那么,我们就需要一个可以插入和查找的数据结构
。那么很容易想到权值线段树。
然后就迎刃而解了。
代码;
#include <cstdio>
#include <algorithm>
#define N 100100
using namespace std;
int n,ans[N],f;
char c;
struct node
{
int h,s;
}p[N];
struct Tree
{
int l,r,num;
}tree[N*3];
int read()
{
f=0;
c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9')
{
f=f*10+c-48;
c=getchar();
}
return f;
}
void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
bool cmp(node x,node y)
{
return x.h<y.h;
}
void make(int x) //建树
{
if (tree[x].l==tree[x].r)
{
tree[x].num=1; //表示这个区间的空位的个数
return;
}
int mid=(tree[x].l+tree[x].r)/2;
tree[x*2].l=tree[x].l;
tree[x*2].r=mid;
tree[x*2+1].l=mid+1;
tree[x*2+1].r=tree[x].r;
make(x*2);
make(x*2+1);
tree[x].num=tree[x*2].num+tree[x*2+1].num;
}
void find(int x,int num,int h) //查询
{
tree[x].num--; //空位减一
if (tree[x].l==tree[x].r)
{
ans[tree[x].l]=h;
return;
}
if (num<tree[x*2].num) find(x*2,num,h); //可以往左就往左
else find(x*2+1,num-tree[x*2].num,h);
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
p[i].h=read(),p[i].s=read();
sort(p+1,p+1+n,cmp);
for (int i=1;i<=n;i++)
if (p[i].s>n-i)
{
printf("impossible");
return 0;
}
tree[1].l=1;
tree[1].r=n;
make(1);
for (int i=1;i<=n;i++)
find(1,min(p[i].s,n-p[i].s-i),p[i].h);
for (int i=1;i<=n;i++)
{
write(ans[i]);
putchar(32);
}
return 0;
}