ARC149E 做題記錄

Lgx_Q發表於2024-09-09

link

題目看起來很嚇人,似乎無從下手。可以看成一個優先佇列,每次加入一個數,彈出最小值。

注意到 \(K\) 範圍為 \(10^9\),嘗試從化簡 \(K\) 範圍入手

發現當 \(K > N - M + 1\) 時,數字 \(N - M + 2 \dots N\) 始終處於優先佇列中,並在最後有序排成一段。

當操作完 \(N - M + 1\) 次後,接下來 \(K - (N - M + 1)\) 次操作中,每次相當於從後面取一個數放到這 \(M - 1\) 個數的前面。

所以,對於 \(K > N - M + 1\) 的情況,我們可以轉化為 \(K = N - M + 1\) 的情況。這樣化簡還有一個好處,就是可以斷環為鏈。

這樣,問題的模型就很簡潔了,去掉了許多繁瑣的條件

對於第 \(i\) 次操作,發現 \(b_i\) 取值為 \(\min(a_{1\dots i + K - 2} \text{ 中的第 } i \text{ 小值 }, a_i)\)

這樣,當 \(b_i\) 為字首最大值,有 \(K\) 個空位供選擇,否則只能是 \(a_i\)

最後再乘上 \(N - M + 2 \dots N\) 這些數的排列方案數 \((M - 1) !\),時間複雜度 \(O(N)\)

點選檢視程式碼
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
using namespace std;
const ll maxn = 3e5 + 10, mod = 998244353;
ll n, m, k, a[maxn], b[maxn], ans = 1;
int main() {
	scanf("%lld%lld%lld", &n, &m, &k);
	for(ll i = 0; i < n; i++) scanf("%lld", a + i);
	if(k > n - m + 1) {
		ll t = k - (n - m + 1); memcpy(b, a, sizeof a);
		for(ll i = 1; i < m; i++)
			a[i + n - m] = b[(i + t + n - m) % n];
		for(ll i = 0; i <= n - m; i++)
			a[i] = b[(i + (t + n - m - i) 
			/ (n - m + 1) * (n - m + 1)) % n];
		k = n - m + 1;
	}
	for(ll i = n; i; i--) a[i] = a[i - 1];
	ll ok = 1;
	for(ll i = 1; i < m; i++)
		ok &= (a[k + i] > a[k + i - 1]);
	for(ll i = 1; i <= k; i++)
		ok &= (a[i] < a[k + 1]);
	if(!ok) { puts("0"); return 0; }
	for(ll i = 1, mx = 0; i <= k; i++)
		if(a[i] > mx) ans = ans * m %mod, mx = a[i];
	for(ll i = 1; i < m; i++) ans = ans * i %mod;
	printf("%lld", ans);
	return 0;
}