题意转:
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
*/