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;
}