Jzoj5456【NOIP2017提高A组冲刺11.6】奇怪的队列

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

nodgd觉得,即使这样明天也能恢复出一个排队顺序,使得任意一个人的两个方向中至少有一个方向上的比他高的人数和他记住的数字相同。可惜n比较大,显然需要写个程序来解决,nodgd很忙,写程序这种事情就交给你了。

如果有多个答案满足题意,输出字典序最小。如果不存在满足题意的排列,输出“impossible”(不含引号)。

第三道友善的第二题(连续3天只A了第二题。。。)

先考虑如果全部人都记对的情况

显然所有人从小到大排序,可以二分找第rank[i]+1个空位

让后加上可以从后面数的情况,那么可以二分出另外一个空位,两者取较前的就好了

这里用了二分+BIT,其实可以线段树的,一个log

#include
#include
#include
using namespace std;
int n,e[100010]; bool b[100010];
struct dt{ int h,c; } s[100010];
struct BIT{
	int w[100010],S;
	inline void add(int x){ for(b[x]=1;x<=n;x+=x&-x) ++w[x]; }
	inline int sum(int x){ for(S=0;x;x&=x-1) S+=w[x]; return S; }
} w;
inline bool c1(dt a,dt b){ return a.hint find(int k){
	if(k<0) return 0;
	int l=1,r=n;
	for(int m;l1>>1;
		if(m-w.sum(m-1)-1>k) r=m-1;
		else l=m;
	}
	return b[l]?0:l;
}
int main(){
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout); 
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d%d",&s[i].h,&s[i].c);
	sort(s+1,s+1+n,c1);
	for(int x,y,i=1;i<=n;++i){
		if(s[i].c>n-i) return 0&puts("impossible");
		x=find(s[i].c);
		y=find(n-i-s[i].c);
		if(!x && !y) return 0&puts("impossible");
		else if(!(x&&y)){ w.add(x+y); e[x+y]=s[i].h; }
		else { w.add(min(x,y)); e[min(x,y)]=s[i].h; }
	}
	for(int i=1;i<=n;++i) printf("%d ",e[i]);
}

你可能感兴趣的:(OI,数据结构,----树状数组,----线段树,求解策略,----贪心,----二分/三分,Jzoj)