Loading

ARC149E 做题记录

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;
}
posted @ 2024-09-09 21:15  Lgx_Q  阅读(15)  评论(0编辑  收藏  举报