Live2D

Solution -「LOCAL」「cov. 牛客多校 2020 第三场 I」礼物

Description

  给定排列 {an},求字典序第 K 大的合法排列 {bn}。称一个排列 {pn} 合法,当且仅当依次将 [1,m],[2,m+1],,[nm+1,n] 内的 p 升序排列后,得到的排列为 {an} 相同。

  n2×106m100K2×1016

Solution

  应该说是构造题吧,想到几乎所有结论却打不出分 qwq。

  显然,bi{an} 中的下标属于集合 [max{1,im+1},n],反过来,ai{bn} 中对应的下标属于集合 [1,min{i+m1,n}]

  然后可以发现 {an} 中的逆序对非常特殊。有性质:

(j[1,i))(ai<aj)bi+m1=ai

  归纳证明。考虑一对 (i,j),满足 maxk(i,j){ak}<aj<ai,若 i+1=j,显然;否则对于 k(i,j),已有 bk+m1=ak,不妨设 bx=aj,则 x[i+m,j+m2],而 x[1,min{j+m1,n}],又有 xi+m,所以 x=j+m1 成立。

  所以可以直接确定所有存在逆序对的 j 的位置。接下来考虑 {an} 是一个单增排列的情况。

  从左往右构造 {bn},我们需要求出固定 {bn} 的前缀时所有合法 {bn} 的方案数。不妨设固定前 i 位,对于一个没有出现的 aj,其放置方案数显然为 min{j+m1,n}i。乘法原理就可以得到所求,最后类似多叉树求第 K 大地枚举就好,复杂度 O(n3)

  由于方案数是指数增长,所以前面很多位直接从小到达钦定,再对后缀的 O(logmn) 暴力构造即可。

  复杂度 O(n+log3n)

Code

/* Clearink */

#include <cstdio>
#include <algorithm>

typedef long long LL;

inline LL rint () {
	LL x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

template<typename Tp>
inline void wint ( Tp x ) {
	if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
	if ( 9 < x ) wint ( x / 10 );
	putchar ( x % 10 ^ '0' );
}

inline int min_ ( const int a, const int b ) { return a < b ? a : b; }

const int MAXN = 2e6;
int n, m, a[MAXN + 5];
LL K;
int top, stk[MAXN + 5], ans[MAXN + 5];
bool used[MAXN + 5];

inline void setnxt ( int& pos, const int x ) { for ( ; ans[pos]; ++ pos ); ans[pos] = x; }

int main () {
	freopen ( "gift.in", "r", stdin );
	freopen ( "gift.out", "w", stdout );
	n = rint (), m = rint (), K = rint ();
	for ( int i = 1; i <= n; ++ i ) a[i] = rint ();
	for ( int i = 1; i <= n; ++ i ) {
		if ( a[i] < a[stk[top]] ) ans[i + m - 1] = a[i];
		else stk[++ top] = i;
	}
	int mut = top, pos = 1;
	for ( LL all = 1; mut && all < K; -- mut ) {
		all *= min_ ( m, top - mut + 2 );
	}
	for ( int i = 1; i < mut; ++ i ) setnxt ( pos, a[stk[i]] );
	for ( int i = mut; i <= top; ++ i ) {
		for ( int j = mut; j <= top; ++ j ) {
			if ( used[j] ) continue;
			used[j] = true;
			LL all = 1;
			for ( int k = mut, pre = i; k <= top; ++ k ) {
				if ( !used[k] ) {
					all *= min_ ( top, k + m - 1 ) - pre ++;
				}
			}
			if ( all < K ) K -= all, used[j] = false;
			else { setnxt ( pos, a[stk[j]] ); break; }
		}
	}
	for ( int i = 1; i <= n; ++ i ) {
		wint ( ans[i] );
		putchar ( i ^ n ? ' ' : '\n' );
	}
	return 0;
}
posted @   Rainybunny  阅读(111)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示