Solution -「LOCAL」「cov. 牛客多校 2020 第三場 I」禮物
阿新 • • 發佈:2020-10-04
\(\mathcal{Description}\)
給定排列 \(\{a_n\}\),求字典序第 \(K\) 大的合法排列 \(\{b_n\}\)。稱一個排列 \(\{p_n\}\) 合法,當且僅當依次將 \([1,m],[2,m+1],\cdots,[n-m+1,n]\) 內的 \(p\) 升序排列後,得到的排列為 \(\{a_n\}\) 相同。
\(n\le2 \times 10^6\),\(m\le 100\),\(K\le2 \times 10^{16}\) 。
\(\mathcal{Solution}\)
應該說是構造題吧,想到幾乎所有結論卻打不出分 qwq。
顯然,\(b_i\)
然後可以發現 \(\{a_n\}\) 中的逆序對非常特殊。有性質:
\[ (\exists j\in[1,i))(a_i<a_j) \Rightarrow b_{i+m-1}=a_i \]歸納證明。考慮一對 \((i,j)\),滿足 \(\max_{k\in(i,j)}\{a_k\}<a_j<a_i\),若 \(i+1=j\),顯然;否則對於 \(k\in(i,j)\)
所以可以直接確定所有存在逆序對的 \(j\) 的位置。接下來考慮 \(\{a_n\}\) 是一個單增排列的情況。
從左往右構造 \(\{b_n\}\),我們需要求出固定 \(\{b_n\}\) 的字首時所有合法 \(\{b_n\}\) 的方案數。不妨設固定前 \(i\) 位,對於一個沒有出現的 \(a_j\),其放置方案數顯然為 \(\min\{j+m-1,n\}-i\)
由於方案數是指數增長,所以前面很多位直接從小到達欽定,再對字尾的 \(\mathcal O(\log_mn)\) 暴力構造即可。
複雜度 \(\mathcal O(n+\log^3n)\)。
\(\mathcal{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;
}