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

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 !
评论
  目录