Description
给定一个长度为 (n) 的数列 (a_i),每个数互不相同,每轮会从剩余的部分中取出一个下标序列字典序最小的值上升的子序列。按轮输出取出的数列。
Solution
先离散化一下
核心观察是:假如我们在 ([1,i]) 中构建了两个序列,那么结束位置早的那个序列的末尾元素一定比晚的那一个要大
这个观察提供了可二分性
我们从左到右扫一遍整个序列,同时用一个 set 维护所有的末尾元素值,每次在末尾元素集合里二分来决定这个元素接在某个的后面还是另开一个
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
int n,a[N],b[N],f[N],ind;
set<int> s;
map<int,int> mp;
vector<int> g[N];
signed main() {
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) mp[a[i]]++;
for(auto &i:mp) i.second=++ind, b[ind]=i.first;
for(int i=1;i<=n;i++) a[i]=mp[a[i]];
for(int i=1;i<=n;i++) {
auto p=s.lower_bound(a[i]);
if(p!=s.begin()) {
--p;
int x=*p;
f[a[i]]=x;
s.erase(p);
}
s.insert(a[i]);
}
for(int i=1;i<=n;i++) {
g[f[i]].push_back(i);
}
for(int t=1;t<=n;t++) {
int i=a[t];
if(f[i]) continue;
cout<<b[i]<<" ";
while(g[i].size()) {
i=g[i][0];
cout<<b[i]<<" ";
}
cout<<endl;
}
}