[2021.7.16] 洛谷七月月赛 题目 天体探测仪(Astral Detector)

传送门

天体探测仪(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;
}