题目

传送门

天体探测仪(Astral Detector)

解法

当时想了很久如何通过每层区间依次查找数值下标,然而半天只会推 \(1\)...


实在不知道是怎么想到的,我们令 \(i\) 为最小值的最大区间为 \([l_i,r_i]\),令 \(len_i=r_i-l_i+1\)

事实上,\(len_i\) 就是最大的能满足 \(i\in S_j\)\(j\)

那么 \(i\) 在排列中的位置一定在 \([l_i,r_i]\) 内,令其为 \(l_i+pos\)(不妨设 \(i\)\(l_i\) 的距离大于与 \(r_i\) 的距离)。考虑 \(j\le 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;
}
posted on 2021-07-16 08:57  Oxide  阅读(84)  评论(0编辑  收藏  举报