【解题报告】洛谷P7726 天体探测仪

【解题报告】洛谷P7726 天体探测仪

思路

对于排列中的一个数字 \(i\) ,设以 \(i\) 为区间最小值的最长的区间是 \([l_i,r_i]\)

首先考虑:如果已知 \(l_i,r_i\) ,如何求出来 \(i\) 的具体位置

\(len_i=r_i-l_i+1\) 则对于某个 \(j \le len_i\) 这段区间里面就一共有 \(len_i-j+1\) 个长度为 \(j\) 的子区间,如果所有长度为 \(j\) 的区间中都包括 \(i\) ,那么 \(i\)\(S_j\) 中就应该出现了 \(len_i-j+1\) 次,反之,它在这个集合中的出现次数小于 \(len_i-j+1\)

因此,我们可以找到最大的 \(j\) 使得 \(i\)\(S_j\) 中出现的次数 \(\le len_i-j+1\) 次,那么 \(i\) 到区间边界的距离就是 \(j\) ,也就是说,\(i\) 到区间边界的距离就应该是 \(j\) ,也就是说, \(i\) 的位置就是 \(l_i+j\) 或者 \(r_i-j\)

这两个位置是等价的,因为区间整体反转一下也不会对答案产生影响

我们发现上面的分析中只用到了 \(len_i\) ,而 \(l_i,r_i\) 仅仅用来定位,所以我们只要设法求出 \(len_i\) 就可以了

\(len_i\) 就是使得 \(i \in S_k\) 最大的 \(k\) ,容易求出

实现的时候,我们首先求出来 \(len_i\)\(i\) 在以它为最小值的区间中的位置,然后从小到大扫描一个 \(i\) ,每次将 \(i\) 放到一个长度为 \(len_i\) 的没有用的区间中

考虑如何维护这个闲置的区间呢?

用一堆队列来维护所有长度为 \(i\) 的闲置区间的左端点,一开始的时候只有 \(1 \in q_n\) 每次确定一个数字之后就会将一个闲置的区间分成两部分

时间复杂度 \(O(n^2)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=805;
int n,k;
int a[maxn];
int seg[maxn][maxn];
queue <int> q[maxn];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n-i+1;j++)
		{
			int x;
			cin>>x;
			seg[i][x]++;
		}
	}
	q[n].push(1);
	for(int x=1;x<=n;x++)//枚举选择的数字 
	{
		int len=0;
		for(int i=n;i>=1;i--)
		{
			if(seg[i][x]>0)
			{
				len=i;//找到一个长度为i的区间存在x 
				break;
			}
		}
		int k=0;
		for(int j=1;j<=len;j++)
		{
			if(seg[j][x]==len-j+1)//看是否在某个区间的长度里面在所有的区间里面出现了 
			{
				k=j-1;//找到一个小于使得这个这个区间的长度 
				break;
			}
		}
		int l=q[len].front();//长度为len的可以放置的下一个区间的最小坐标 
		q[len].pop();//取出第一个数字 
		a[l+k]=x;//
		if(k>0)//如果k的区间长度大于1的话 
		q[k].push(l); //把这个东西放回去
		if(len-k-1>0)//如果减去这个之后的区间长度大于1 
		q[len-k-1].push(l+k+1);//那么更改放的位置,我们新的坐标改成这个 
	}
	for(int i=1;i<=n;i++)
	cout<<a[i]<<" ";
	cout<<'\n';
	return 0;
}
posted @ 2021-10-13 19:01  wweiyi  阅读(56)  评论(0编辑  收藏  举报
js脚本