AtCoder Regular Contest 114 F Permutation Division

发布时间 2023-04-22 08:46:36作者: zltzlt

洛谷传送门

AtCoder 传送门

这题居然是之前某场模拟赛(contest 701)的 T1……(@Vidoliga 场切但是被卡常/bx)

下面记 \(m\) 为原题面中的 \(K\)\(a_i\) 为原题面中的 \(P_i\)

不难发现后手的策略是把所有段按照段的第一个数从大到小排序接在一起。

考虑若 \(a_1 \le m\),则先手能把所有 \(\le m\) 的数都作为段头。

\(a_1 > m\),操作后的排列字典序一定大于等于原排列(后手可以选择不动)。于是先手想让与原序列的 \(\text{LCP}\) 尽量大。

预处理出以 \(a_1\) 开头,\(a_i\) 结尾的 \(\text{LDS}\),记为 \(f_i\)。考虑枚举 \(i\),那么 \(i\)\(i\) 前面有 \(f_i\) 个元素可以作为段的开头,设它们为 \(1 = p_1 < p_2 < \cdots < p_{f_i} = i\),则后面还需要 \(m - f_i\) 段。找到后面第一个位置 \(k\) 使得 \(\sum\limits_{x=k+1}^n [a_x < a_i] = m - f_i\),那么 \(k\) 就是前面分成 \(f_i\)\(p_1 \sim p_{f_i}\) 这种方案的 \(\text{LCP}\)。注意如果 \(\text{LCP}\) 相同,则先手想让 \(k + 1 \sim n\) 还需要分的段尽量少(即 \(m - f_i\) 尽量小)。

\(k\) 可以使用主席树+二分,因此时间是 \(O(n \log^2 n)\) 的。

code
// Problem: F - Permutation Division
// Contest: AtCoder - AtCoder Regular Contest 114
// URL: https://atcoder.jp/contests/arc114/tasks/arc114_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 200100;

int n, m, a[maxn], f[maxn];

inline bool cmp(const pii &x, const pii &y) {
	return a[x.fst] > a[y.fst];
}

namespace BIT {
	int c[maxn];
	
	inline void update(int x, int d) {
		for (int i = x; i; i -= (i & (-i))) {
			c[i] = max(c[i], d);
		}
	}
	
	inline int query(int x) {
		int res = 0;
		for (int i = x; i <= n; i += (i & (-i))) {
			res = max(res, c[i]);
		}
		return res;
	}
}

int rt[maxn], ls[maxn << 6], rs[maxn << 6], tree[maxn << 6], ntot;

int build(int l, int r) {
	int rt = ++ntot;
	if (l == r) {
		return rt;
	}
	int mid = (l + r) >> 1;
	ls[rt] = build(l, mid);
	rs[rt] = build(mid + 1, r);
	return rt;
}

int update(int rt, int l, int r, int x) {
	int u = ++ntot;
	ls[u] = ls[rt];
	rs[u] = rs[rt];
	tree[u] = tree[rt] + 1;
	if (l == r) {
		return u;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) {
		ls[u] = update(ls[u], l, mid, x);
	} else {
		rs[u] = update(rs[u], mid + 1, r, x);
	}
	return u;
}

int query(int rt, int l, int r, int ql, int qr) {
	if (ql > qr) {
		return 0;
	}
	if (ql <= l && r <= qr) {
		return tree[rt];
	}
	int mid = (l + r) >> 1, res = 0;
	if (ql <= mid) {
		res += query(ls[rt], l, mid, ql, qr);
	}
	if (qr > mid) {
		res += query(rs[rt], mid + 1, r, ql, qr);
	}
	return res;
}

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	if (a[1] <= m) {
		vector<pii> vc;
		for (int i = 1, j = 1; i <= n; i = (++j)) {
			while (j < n && a[j + 1] > m) {
				++j;
			}
			vc.pb(i, j);
		}
		sort(vc.begin(), vc.end(), cmp);
		for (pii p : vc) {
			for (int i = p.fst; i <= p.scd; ++i) {
				printf("%d ", a[i]);
			}
		}
		return;
	}
	rt[n + 1] = build(1, n);
	for (int i = n; i; --i) {
		rt[i] = update(rt[i + 1], 1, n, a[i]);
	}
	f[1] = 1;
	BIT::update(a[1], 1);
	for (int i = 2; i <= n; ++i) {
		if (a[i] > a[1]) {
			continue;
		}
		f[i] = BIT::query(a[i] + 1) + 1;
		BIT::update(a[i], f[i]);
	}
	int lcp = 0, num = 0;
	for (int i = 1; i < n; ++i) {
		if (a[i] > a[1] || query(rt[i + 1], 1, n, 1, a[i] - 1) < m - f[i]) {
			continue;
		}
		int l = i, r = n - 1, pos = -1;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (query(rt[mid + 1], 1, n, 1, a[i] - 1) >= m - f[i]) {
				pos = mid;
				l = mid + 1;
			} else {
				r = mid - 1;
			}
		}
		if (pos != -1 && (pos > lcp || (pos == lcp && f[i] > f[num]))) {
			lcp = pos;
			num = i;
		}
	}
	for (int i = 1; i <= lcp; ++i) {
		printf("%d ", a[i]);
	}
	vector<pii> vc;
	for (int i = lcp + 1, j = lcp + 1; i <= n; i = (++j)) {
		while (j < n && a[j + 1] > a[num]) {
			++j;
		}
		vc.pb(i, j);
	}
	sort(vc.begin(), vc.end(), cmp);
	for (pii p : vc) {
		for (int i = p.fst; i <= p.scd; ++i) {
			printf("%d ", a[i]);
		}
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}