GYM 105125 C

题目描述

给定 \(NM\) 个数 \(A_1,A_2,\dots,A_{NM}\),你要将这些数分成 \(N\) 个数组,每个数组 \(M\) 个数。接着你要将这些数组按字典序排序。

对于排序后每个数组求出可能的字典序最小情况。

思路

我们从字典序的比较上来考虑,并把 \(A\) 排序。

首先考虑当前数组 \(i\) 的第一位。在 \(i\) 之前,还有 \(i-1\) 个数组的字典序需要小于它,因此第一个数一定为 \(A_i\)

接着考虑第 \(2\) 位,如果某个数组第一位就小于 \(i\) 了,那么后面的部分就不用管了。假设有 \(x\) 个数组第一位与 \(i\) 相同,也就是在 \([1,i]\) 中有 \(x\) 个数 \(=A_i\),那么就有 \(x-1\) 个数组目前还 \(=\) 数组 \(i\),那么 \([i+1,i+x-1]\) 都要分配给它们,所以第二位为 \(A_{i+x}\)

后面的部分同理。

空间复杂度 \(O(NM)\),时间复杂度 \(O(NM\log (NM))\)

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1000001;

int n, m, a[MAXN];
vector<int> ve[MAXN];

int query(int l, int r, int x) {
  int L = lower_bound(ve[x].begin(), ve[x].end(), l) - ve[x].begin();
  int R = upper_bound(ve[x].begin(), ve[x].end(), r) - ve[x].begin() - 1;
  return R - L + 1;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> m;
  for(int i = 1; i <= n * m; ++i) {
    cin >> a[i];
  }
  sort(a + 1, a + n * m + 1);
  for(int i = 1; i <= n * m; ++i) {
    ve[a[i]].push_back(i);
  }
  for(int i = 1; i <= n; ++i) {
    int cnt = i, pos = i;
    for(int j = 1; j <= m; ++j) {
      cout << a[pos] << " \n"[j == m];
      cnt = min(cnt, query(pos - cnt + 1, pos, a[pos]));
      pos += cnt;
    }
  }
  return 0;
}
posted @ 2024-09-17 21:46  Yaosicheng124  阅读(6)  评论(0编辑  收藏  举报