HDU 5493 Queue (合肥网络赛 1010 )

题目链接

题意,一共有 N 个人,每个人的身高都不同,  知道其中每个人的高度 h 以及在他前面或者后面有 k 个人比他高, 问原先的队列, 如果有多种站法, 求按身高字典序最小的。

做法,因为每个人的身高不同, 所以我们先将问题简化, 假设 N 个人的身高分别是 1, 2, 3 ... N 。

 然后我们倒过来将人插到原先站好的序列中还原原序列。假设当前还原到身高为 N - 2 的这个人,已知站在他前面或者后面的人有 2 个。那么在这之前我们处理好的人有 N - 1 和 N, 因为这两个人都比 N - 2 高, 所以这两个人怎么站的对于 N - 2 来说没有影响。同理, N - 2 站的位置相对于之前两人对于 1 ~ N - 3 这些人来说也没有影响。 因为无论如何他们三人总是最高的三人。 那么做法就比较明确了, 倒过来还原序列, 第 i 个处理到的人插到当前序列的 min(k, i - 1 - k ) 的位置中。 

因为序列是不断扩张的, 所以它们的数组下标也不断在变化, 这样寻找当前序列的位置就有些困难, 那么可以顺着用一个树状数组 + 二进制前缀和的方式维护当前位置 pos 在没处理的位置中的位置。


参考代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 100005;

int n, s[N], T[N];

int lowbit(int x) {
  return x & -x;
}

void add(int x) {
  for (; x <= n; x += lowbit(x)) T[x]++;
}

void sub(int x) {
  for (; x <= n; x += lowbit(x)) T[x]--;
}

int sum(int x) {
  int ans = 0;
  for (; x; x -= lowbit(x)) ans += T[x];
  return ans;
}

int get(int w, int l, int r) {
  if (l == r) return l;
  int mid = (l + r) >> 1;
  if (sum(mid) >= w) return get(w, l, mid);
  return get(w, mid + 1, r);
}


struct node {
  int h, p;

  node() {}

  node(int h, int p) : h(h), p(p) {}

  bool operator < (const node & a) const {
    return h < a.h;
  }

  void read() {
    scanf("%d %d", &h, &p);
  }
} a[N];

int CASE, b[N];

int main() {
  int t;
  scanf("%d", &t);
  while (t--) {
    memset(T, 0, sizeof T);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
      add(i);
      a[i].read();
    }
    sort(a + 1, a + n + 1);
    int FLAG = 1;
    for (int i = 1; i <= n; i++) {
      int taller = n - i;
      if (a[i].p > taller) {
        FLAG = 0;
        break;
      }
      int pos = min(a[i].p, taller - a[i].p) + 1;
      int p = get(pos, 1, n);
      b[p] = a[i].h;
      sub(p);
    }
    printf("Case #%d:", ++CASE);
    if (!FLAG) {
      puts(" impossible");
      continue;
    }
    for (int i = 1; i <= n; i++) printf(" %d", b[i]);
    puts("");
  }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值