正题
题目链接:https://www.luogu.com.cn/problem/P7726
题目大意
一个长度为nnn的排列,给出nnn个可重集SiS_iSi表示所有长度为iii的区间的最小值构成的集合。
求构造这个排列。
1≤n≤8001\leq n\leq 8001≤n≤800
解题思路
对于一个数字,如果在SiS_iSi中的出现次数小于iii时,证明包含它的区间中拥有不是它为最小值的情况。
所以每个数字我们找到出现次数小于iii的第一个SiS_iSi,那么它离它左右两边比他小的数字的距离就是i−1i-1i−1。
然后考虑再求出另一边的距离,当某个时候SiS_iSi中不包含数字xxx时,那么证明xxx距离两边的距离和小于iii,找到一个位置就可以算出另一边的距离。
然后直接从小到大找满足条件的位置插入即可。
时间复杂度O(n2)O(n^2)O(n2)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int N=810;
int n,c[N],L[N],R[N],ans[N];
set<int> s;
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){int x;memset(c,0,sizeof(c));for(int j=1;j<=n-i+1;j++){scanf("%d",&x);c[x]++;}for(int j=1;j<=n;j++){if(!L[j]&&c[j]!=i)L[j]=i-1;if(!R[j]&&!c[j])R[j]=i-L[j];}}R[1]=n-L[1]+1;s.insert(0);s.insert(n+1);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(ans[j])continue;int l=(*s.lower_bound(j))-j;int r=j-*(--s.upper_bound(j));if(l>r)swap(l,r);if(l==L[i]&&r==R[i]){ans[j]=i;s.insert(j);break;}}}for(int i=1;i<=n;i++)printf("%d ",ans[i]);
}