好排列 题解
[B] 好排列 题解
测试点 1 \sim 5
直接模拟操作过程即可。对每个数,我们考虑其位置,并用
时间复杂度:
测试点 6 \sim 9, 18 \sim 19
发现可以通过前缀和与后缀和优化枚举过程,因此可以在每次考虑位置前计算好每个
时间复杂度:
测试点 10 \sim 17, 20 \sim 25
下面将
首先,一个数字只会选择一段连续的空位中的中间位置(靠两边的一定更劣)。特别地,如果连续空位长度
注意到一个数字进入某个位置将断开一个连续空位段,同时可能影响至多两个相邻的空位段。
考虑维护一个链表处理哪些段相邻,再用一个堆维护哪个段最优。这个堆每次需要更新其中至多两个元素,需要手写。注意每更新一个元素就要更新一次堆,否则堆的性质会被破坏。
也可以考虑用 set 维护,不过常数较大,不推荐。
时间复杂度:
正解代码:
#include <iostream>
#include <memory.h>
using namespace std;
inline void train() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
constexpr int N = 1e6+3;
inline int maxi(int a, int b) {
return a > b ? a : b;
}
inline int mini(int a, int b) {
return a < b ? a : b;
}
int nextfree[N], prevfree[N];
bool occupied[N];
inline int right_dis(int pos) {
return nextfree[pos] - pos - 1;
}
inline int left_dis(int pos) {
return pos - prevfree[pos] - 1;
}
struct range {
int l, r;
bool ok = false;
range() {
}
range(int l, int r) : l(l), r(r), ok(true) {
}
int len() const {
return r-l+1;
}
int dis() const {
if (len() > 2) return 0;
else if (len() == 1) return left_dis(l) + right_dis(l);
else return mini(left_dis(l), right_dis(r));
}
int pos() const {
if (len() > 2) return (l+r)/2;
else if (len() == 1) return l;
else {
if (left_dis(l) <= right_dis(r)) return l;
else return r;
}
}
};
range heap[N];
int lat[N], rat[N];
int heaprear = 1;
inline void clear() {
heaprear = 1;
memset(lat, -1, sizeof(lat));
memset(rat, -1, sizeof(rat));
memset(occupied, 0, sizeof(occupied));
memset(heap, 0, sizeof(heap));
}
bool operator < (const range &a, const range &b) {
if (!a.ok) return false;
else if (!b.ok) return true;
// 中间位置上至少有一边有人
if (a.len() <= 2 && b.len() <= 2) {
int maxa = a.dis(), maxb = b.dis();
if (maxa == maxb) {
return a.l < b.l;
}
return maxa < maxb;
}
int adiv = a.len() / 2 + (a.len() % 2), bdiv = b.len() / 2 + (b.len() % 2);
if (adiv == bdiv) return a.l < b.l;
return adiv > bdiv;
//return a.len() > b.len();
}
inline void swaps(range &a, range &b) {
static range tmp; // or range;
tmp = a;
a = b;
b = tmp;
}
inline void swaps(int &a, int &b) {
static int tmp;
tmp = a;
a = b;
b = tmp;
}
void adjust(int pos) {
if (pos < 1) return;
auto &self = heap[pos];
auto &father = heap[pos>>1];
auto &lc = heap[pos<<1];
auto &rc = heap[(pos<<1)|1];
if (pos > 1 && heap[pos] < father) {
lat[self.l] = pos>>1;
rat[self.r] = pos>>1;
lat[father.l] = pos;
rat[father.r] = pos;
swaps(father, self); // Move father to here and here to father
adjust(pos>>1);
} else if ((pos<<1) < heaprear) {
if (lc < self && lc.ok) {
if (rc < lc) {
lat[self.l] = (pos << 1) | 1;
rat[self.r] = (pos << 1) | 1;
lat[rc.l] = pos;
rat[rc.r] = pos;
swaps(rc, self);
adjust((pos << 1) | 1);
}
else {
lat[self.l] = pos << 1;
rat[self.r] = pos << 1;
lat[lc.l] = pos;
rat[lc.r] = pos;
swaps(lc, self);
adjust(pos << 1);
}
} else if (rc < self && rc.ok) {
lat[self.l] = (pos<<1)|1;
rat[self.r] = (pos<<1)|1;
lat[rc.l] = pos;
rat[rc.r] = pos;
swaps(rc, self);
adjust((pos<<1)|1);
}
}
}
inline void occupy(int pos) {
occupied[pos] = true;
prevfree[nextfree[pos]] = prevfree[pos]; // To calculate the dsitance to know
adjust(lat[nextfree[pos]]);
nextfree[prevfree[pos]] = nextfree[pos];
adjust(rat[prevfree[pos]]);
}
void pushin(range object) {
if (object.len() <= 0) return;
object.ok = true;
lat[object.l] = heaprear;
rat[object.r] = heaprear;
heap[heaprear] = object;
heaprear++;
adjust(heaprear - 1);
}
range popout() {
range result = heap[1];
heap[1].ok = false;
lat[heap[1].l] = -1;
rat[heap[1].r] = -1;
swaps(heap[heaprear - 1], heap[1]);
heaprear--;
lat[heap[1].l] = (1 <= heaprear) ? -1 : 1;
rat[heap[1].r] = (1 <= heaprear) ? -1 : 1;
adjust(1);
return result;
}
inline void execute() {
int n,k;
cin>>n>>k;
if (k <= 2) {
if (k == 1) cout<<"1";
else cout<<n;
cout<<endl;
return;
}
k-=2;
for (int i = 2; i <= n-1; i++) {
nextfree[i] = (i == n-1) ? n+1 : (i+1);
prevfree[i] = (i == 2) ? 0 : (i-1);
}
occupied[1] = occupied[n] = true;
pushin(range(2, n-1));
for (int step = 1; step < k; step++) {
range cur = popout();
int cp = cur.pos();
occupy(cp);
if (cur.len() > 1) {
pushin(range(cur.l, cp-1));
pushin(range(cp+1, cur.r));
}
}
range cur = popout();
int cp = cur.pos();
cout<<cp<<endl;
//cout<<flush;
// return 0;
}
int main() {
train();
int t;
cin>>t;
for (int _ = 0; _ < t; _++) {
clear();
execute();
}
return 0;
}
另外,本题有一些神秘的部分分,没有细讲是因为出题人不会或不想写。
- 测试点
10 \sim 17 给某人曾经提出过的玄学数论做法。 - 内存限制
512 \text{MB} 给某些开一大堆线段树等数据结构的做法。