题意:
nodgd的粉丝太多了,每天都会有很多人排队要签名。
今天有n个人排队,每个人的身高都是一个整数,且互不相同。很不巧,nodgd今天去忙别的事情去了,就只好让这些粉丝们明天再来。同时nodgd提出了一个要求,每个人都要记住自己前面与多少个比自己高的人,以便于明天恢复到今天的顺序。
但是,粉丝们或多或少都是有些失望的,失望使她们晕头转向、神魂颠倒,已经分不清楚哪一边是“前面”了,于是她们可能是记住了前面比自己高的人的个数,也可能是记住了后面比自己高的人的个数,而且他们不知道自己记住的是哪一个方向。
nodgd觉得,即使这样明天也能恢复出一个排队顺序,使得任意一个人的两个方向中至少有一个方向上的比他高的人数和他记住的数字相同。可惜n比较大,显然需要写个程序来解决,nodgd很忙,写程序这种事情就交给你了。
这题是个水题,比赛的时候想复杂了导致我怒刚2个小时没刚出来。。
设suf,pre表示每个点前缀后缀有多少个比他大,用两个bit维护,问题就在于我不是插入,而是每次交换,计算每次交换对于i,j之间的数的影响,然后除此之外还要看看是否能交换,因为有两个位置可交换,如果两个位置都不能交换那么肯定impossible,然后二分查找位置,判断用bit处理,所以时间复杂度nlog^2.
以上是我的比赛想法,太过复杂,而且实现细节很多,再加上比赛机子c++直接爆炸,只能输出调试,用拍还不能调用文件,导致我根本调不出来,直接GG。
事实上我也应该注意到这种做法实在是太过复杂,尤其是每次交换的时候不能保证交换完以后已经被交换过的肯定不会被再次交换。反思就是每次想题的时候正解想到是没什么问题的,问题在于我经常把代码复杂化,很多能简化的地方要多思考一下,不要着急上手。
好了现在讲正解。事实上根本没必要死死在原序列交换来交换去,完全可以新开一个答案数组往里面插入,相信想到插入以后这题就很水了,每个数字插入的位置只有两种可能,从小到大插入,如果b[i]>n-i明显不合法,然后用线段树维护区间空位数即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
//map<int,int>pos[N];
int n,m;
struct node
{
int x,y;
}a[N];
struct tree
{
int l,r,size;
}t[N*4+10];
int val[N];
bool cmp(node a,node b)
{
return a.x<b.x;
}
inline void build(int x,int l,int r)
{
t[x].l=l,t[x].r=r;
t[x].size=r-l+1;
if (l==r)return;
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
inline void ins(int x,int k,int v)
{
t[x].size--;
if (t[x].l==t[x].r)
{
val[t[x].l]=v;
return;
}
if (t[x<<1].size>=k)ins(x<<1,k,v);
else ins(x<<1|1,k-t[x<<1].size,v);
}
int main()
{
freopen("queue.in","r",stdin);
freopen("queue.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d%d",&a[i].x,&a[i].y);
}
sort(a+1,a+1+n,cmp);
build(1,1,n);
fo(i,1,n)
{
if (a[i].y>n-i)
{
printf("impossible\n");
return 0;
}
int pos=a[i].y+1;
pos=min(pos,n-a[i].y-i+1);
ins(1,pos,a[i].x);
}
fo(i,1,n)printf("%d ",val[i]);
return 0;
}