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;
}