CF1097E Egor and an RPG game

题目链接

\(f(n)\) 表示最小的正整数 \(k\),使得所有长为 \(n\) 的排列都可以被划分为至多 \(k\) 个单调子序列。

给定长为 \(n\) 的排列 \(p\),构造将其划分为至多 \(f(n)\) 个单调子序列的方案。\(T\) 组数据。

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


结论:

\[f(n)=\max\left\{k\left|\frac{k(k+1)}{2}\le n\right.\right\} \]

证明:设 \(c(n)\) 是上式右边的值。我们可以构造 \(p=\{1,3,2,6,5,4,\cdots\}\),也即分成 \(c(n)\) 组下降子段,每组长度比上一组长 \(1\),只能划分为至少 \(c(n)\) 个单调子序列。可以得到 \(f(n)\ge c(n)\)。然后我们考虑对所有 \(p\) 构造划分为至多 \(c(n)\) 个单调子序列的方法。

求出现在的 LIS,设其长度为 \(l\),若 \(l>c(n)\),直接划分为一组,因为 \(c(n-c(n)-1)\le c(n)-1\),所以转化为了更小的情况,可以归纳证明。

\(l\le c(n)\),根据 Dilworth 定理,可以将其划分为 \(l\) 个下降子序列,实现方法可以用类似二分求 LIS 的方法,维护一些末尾单调上升的下降子序列,每次将 \(p_i\) 放进第一个末尾比 \(p_i\) 大的子序列。然后构造完就做完了,时间复杂度 \(O(n\sqrt n\log n)\),还顺便得到了 \(f(n)=c(n)\)

#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int N = 100003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
} template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int t, n, a[N]; bool vis[N]; pii tr[N];
void upd(int p, pii v){for(;p <= n;p += p & -p) chmax(tr[p], v);}
pii qry(int p){pii res; for(;p;p -= p & -p) chmax(res, tr[p]); return res;}
vector<vi> ans, res; int from[N];
void solve(){ read(n); ans.clear(); memset(vis, 0, n+1);
	for(int i = 1;i <= n;++ i) read(a[i]);
	int k = 0; while((k*(k+1)>>1) <= n) ++ k;
	while(k --){
		memset(tr, 0, n+1<<3);
		for(int i = 1;i <= n;++ i) if(!vis[i]){
			pii tt = qry(a[i]); from[i] = tt.se;
			upd(a[i], MP(tt.fi+1, i));
		} pii tt = qry(n);
		if(tt.fi <= k){ res.clear();
			for(int i = 1;i <= n;++ i) if(!vis[i]){
				if(res.empty() || a[i] > res.back().back()) res.push_back({a[i]});
				else {
					int l = 0, r = res.size()-1;
					while(l < r){
						int mid = l+r>>1;
						if(a[i] > res[mid].back()) l = mid+1;
						else r = mid;
					} res[l].PB(a[i]);
				}
			} for(auto &u : res) ans.PB(u); break;
		} vi lis;
		for(int i = tt.se;i;i = from[i]){lis.PB(a[i]); vis[i] = true;}
		reverse(lis.begin(), lis.end()); ans.PB(lis);
	} printf("%llu\n", ans.size());
	for(auto &u : ans){ printf("%llu", u.size());
		for(int _ : u) printf(" %d", _);
		putchar('\n');
	}
} int main(){read(t); while(t --) solve();}
posted @ 2021-03-11 19:32  mizu164  阅读(84)  评论(0编辑  收藏  举报