题目
天体探测仪(Astral Detector)
解法
当时想了很久如何通过每层区间依次查找数值下标,然而半天只会推 (1)...
实在不知道是怎么想到的,我们令 (i) 为最小值的最大区间为 ([l_i,r_i]),令 (len_i=r_i-l_i+1)。
事实上,(len_i) 就是最大的能满足 (iin S_j) 的 (j)。
那么 (i) 在排列中的位置一定在 ([l_i,r_i]) 内,令其为 (l_i+pos)(不妨设 (i) 与 (l_i) 的距离大于与 (r_i) 的距离)。考虑 (jle len_i),在所有属于 ([l_i,r_i]) 的长度为 (j) 的区间中若都包含 (i),那么 (S_j) 中应有且只有 (len_i-j+1) 个 (i)。容易发现 (pos) 应该是最大的满足属于 ([l_i,r_i]) 的长度为 (j) 的区间中不都包含 (i) 的 (j)。
至此,我们若求得 (l_i,r_i) 中一个就能确定 (i) 的位置了。
再做一个观察,我们发现 (1) 可以将 ([1,n]) 分成两个隔断的小区间,因为 数据保证有解,所以一定有数是这两个小区间的最大值!而且,如果我们从小到大枚举数值,容易得出某数 (i) 的 ([l_i,r_i]) 一定不会包含几个被隔断的区间。
还有一点要说的是,(l_i+pos) 和 (r_i-pos) 其实都是合法解,不过这两种情况隔断的区间长度相同,而且隔断区间实际上是孤立的,所以随便选一个就好了。
基于此,我们可以开一个 vector
(f_i) 维护某个长度为 (i) 的隔断区间可能的左端点(也即维护 (l_i)),这样就在 (mathcal O(n^2)) 内解决了此题。
代码
#include <queue>
#include <cstdio>
using namespace std;
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
const int maxn=805;
int n,cnt[maxn][maxn],tp[maxn];
int ans[maxn];
queue <int> q[maxn];
int main() {
n=read(9);
for(int i=1;i<=n;++i)
for(int j=1;j<=n-i+1;++j) {
int x=read(9);
++cnt[i][x],tp[x]=i;
}
q[n].push(1);
for(int i=1;i<=n;++i) {
int pos=0;
for(int j=tp[i]-1;j;--j)
if(cnt[j][i]<tp[i]-j+1) {
pos=j; break;
}
int len=tp[i];
int p=q[len].front();
q[len].pop();
ans[p+pos]=i;
q[pos].push(p);
q[len-pos-1].push(p+pos+1);
}
for(int i=1;i<=n;++i)
print(ans[i],' ');
puts("");
return 0;
}