一.题目链接:
HDU-5493
二.题目大意:
有 n 个人排队,身高各不相同.
给出每个人的身高,以及队中他前面比他高或者后面比他高的人数.
若此队伍存在,则输出最小字典序的队伍排列.
否则,输出 "impossible".
三.分析:
因为是求最小字典序,所以先对身高排序,之后再向队中加人.
假设第 i (1 ≤ i ≤ n) 个人的身高为 h,队中他前面比他高或者后面比他高的人数为 k.
则有两个位置可供他插入.
① 队中他前面比他高的人数为 k:则插入的位置应为从前往后的第 k + 1 个空位.
① 队中他后面比他高的人数为 k:则插入的位置应为从前往后的第 n - i - k + 1 个空位.
又因为要求最小字典序
所以第 i 个人所插入的位置应为 min(k+ 1, n - i - k + 1).
现在问题分解为了求第 x 个空位的索引.
有两种方法解决.
1 表示空位,0 表示已有人,
① 树状数组 + 二分:树状数组区间求和当做 check()函数,二分每个人的位置,之后再单点更新.
② 线段树:查询第 x 个空位的下标,之后再单点更新.
现在考虑什么情况是有矛盾的.
易得:第 i 个人前面的空位不足 k 且 后面的空位也不足 k.
即:k + i > n.
四.代码实现:
①树状数组 + 二分
#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-8
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long int
using namespace std;
const int M = (int)1e5;
const int inf = 0x3f3f3f3f;
struct node
{
int k;
int h;
}s[M + 5];
int n;
int h[M + 5];
int c[M + 5];
bool cmp(node a, node b)
{
return a.h < b.h;
}
int lowbit(int x)
{
return x & (-x);
}
void update(int x, int v)
{
for(int i = x; i <= n; i += lowbit(i))
c[i] += v;
}
int sum(int x)
{
int ans = 0;
for(int i = x; i > 0; i -= lowbit(i))
ans += c[i];
return ans;
}
int binary1(int i)
{
int l = 1;
int r = n;
while(l < r)
{
int mid = (l + r) >> 1;
if(sum(mid) < s[i].k + 1)
l = mid + 1;
else
r = mid;
}
return r;
}
int binary2(int i)
{
int l = 1;
int r = n;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(sum(n) - sum(mid - 1) < s[i].k + 1)
r = mid - 1;
else
l = mid;
}
return r;
}
int main()
{
int T;
scanf("%d", &T);
for(int ca = 1; ca <= T; ++ca)
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d %d", &s[i].h, &s[i].k);
sort(s + 1, s + n + 1, cmp);
memset(c, 0, sizeof(c));
for(int i = 1; i <= n; ++i)
update(i, 1);
bool flag = 1;
for(int i = 1; i <= n; ++i)
{
if(i + s[i].k > n)
{
flag = 0;
break;
}
int pos = min(binary1(i), binary2(i));
h[pos] = s[i].h;
update(pos, -1);
}
printf("Case #%d: ", ca);
if(flag)
{
for(int i = 1; i <= n; ++i)
printf("%d%c", h[i], i == n ? '\n' : ' ');
}
else
printf("impossible\n");
}
return 0;
}
② 线段树
#include <set>
#include <map>
#include <ctime>
#include <queue>
#include <cmath>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define eps 1e-8
#define lc k * 2
#define rc k * 2 + 1
#define pi acos(-1.0)
#define ll long long int
using namespace std;
const int M = (int)1e5;
const int inf = 0x3f3f3f3f;
int n;
struct node1
{
int h;
int k;
} s[M + 5];
int height[M + 5];
int tree[M * 4 + 5];
bool cmp(node1 a, node1 b)
{
return a.h < b.h;
}
void push_up(int k)
{
tree[k] = tree[lc] + tree[rc];
}
void build(int k, int l, int r)
{
if(l == r)
{
tree[k] = 1;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
push_up(k);
}
int query(int k, int l, int r, int a, int b)
{
if(l >= a && r <= b)
return tree[k];
int mid = (l + r) >> 1;
int ans = 0;
if(a <= mid)
ans += query(lc, l, mid, a, b);
if(mid < b)
ans += query(rc, mid + 1, r, a, b);
return ans;
}
void update(int k, int l, int r, int num, int h)
{
if(l == r)
{
tree[k] = 0;
height[l] = h;
return;
}
int mid = (l + r) >> 1;
if(num <= tree[lc])
update(lc, l, mid, num, h);
else
update(rc, mid + 1, r, num - tree[lc], h);
push_up(k);
}
int main()
{
int T;
scanf("%d", &T);
for(int ca = 1; ca <= T; ++ca)
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d %d", &s[i].h, &s[i].k);
build(1, 1, n);
sort(s + 1, s + n + 1, cmp);
bool flag = 1;
for(int i = 1; i <= n; ++i)
{
if(i + s[i].k > n)
{
flag = 0;
break;
}
update(1, 1, n, min(s[i].k + 1, n - i - s[i].k + 1), s[i].h);
}
printf("Case #%d: ", ca);
if(flag)
{
for(int i = 1; i <= n; ++i)
printf("%d%c", height[i], i == n ? '\n' : ' ');
}
else
printf("impossible\n");
}
return 0;
}