嘘~ 正在从服务器偷取页面 . . .

UOJ278 【UTR #2】题目排列顺序 题解


UOJ278 【UTR #2】题目排列顺序 题解

题目链接:#278. 【UTR #2】题目排列顺序

题意

“又要出题了。” 宇宙出题中心主任 —— cxy ,坐在办公桌前策划即将到来的 UOI。

这场比赛有 \(n\) 道题, cxy 需要决定这些题目的难度,然后再在汪洋大海中寻找符合该难度的题目。

题目的难度可以用一个 \(1\)\(n\)排列 \(a_1, \dots, a_n\) 表示,其中 \(a_i\) 表示第 \(i\) 道题目在这 \(n\) 道题目中是第 \(a_i\) 简单的题目,即恰有 \(a_i - 1\) 道题目比第 \(i\) 道题目简单。

经验丰富的 cxy 早就悟出了一种科学地决定难度顺序的方法。首先, cxy 定义了难度递增子序列为序列 \(a_{p_1}, a_{p_2}, \dots, a_{p_k}\)\(1 \le p_1 < p_2 < \dots < p_k \le n\)) 满足 \(a_{p_1} < a_{p_2} < \dots < a_{p_k}\) 。然后, cxy 决定了 \(n\) 个整数 \(f_1, \dots, f_n\),他希望找出一个难度顺序使得对于每个 \(1 \le i \le n\) 均满足以 \(a_i\) 结尾的难度递增子序列的最长长度恰好为 \(f_i\)

但 cxy 日理万机,于是他找到了你,请你帮助 cxy 构造一个符合条件的 \(a_1, \dots, a_n\) 吧!

输入格式

第一行一个正整数 \(n\)

接下来一行 \(n\) 个数,其中第 \(i\) 个数表示 \(f_i\)。(\(1 \le f_i \le n\)

输出格式

输出一行 \(n\) 个数,表示 \(a_1, \dots, a_n\)

题目保证至少存在一组解。如有多组解,输出任意一组均可。

数据范围

\(1 \le n \le 10^5\)

若题目有解,则 \(f_1 = 1\) ,且若 \(f_i = k (k>1)\) ,则必存在 \(1 \le j < i\) 使得 \(f_j = k-1\)

因此构造序列使得 \(f_i < f_j\) 时必有 \(a_i < a_j\) ;当 \(f_i = f_j\) 时,必有 \(a_i > a_j\)。( \(i < j\)

前者是显然的,后者解释一下。当 \(f_i=f_j\)\(a_i\) 若小于 \(a_j\) ,则 \(f_j\) 应当为 \(k+1\) 。因此这么构造是合法的。

具体地,把 \((f_i,a_i)\) 看作一个 pair<int,int> ,我们可以用计数排序来做,注意要正序做。

时间复杂度 \(\mathcal{O}(n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5+15))

int n,m,a[N],b[N],buf[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i=1; i<=n; i++) { cin >> a[i]; ++buf[a[i]]; up(m, a[i]); }
    for(int i=1; i<=m; i++) buf[i] += buf[i-1];
    for(int i=1; i<=n; i++) cout << (buf[a[i]]--) << " \n"[i==n];
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/?redirect=443


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录