Codeforces Round #789 (Div. 2) F

F. Tokitsukaze and Permutations

题意:
给你长度为n ( n ≤ 1 0 6 ) n(n\leq10^6)n(n106)的排列,可以进行k ( k < n ) k(k<n)k(k<n)次操作,每次操作从1 11遍历到n nn,如果a i > a i + 1 a_i>a_{i+1}ai>ai+1,则交换两个数字。
给你数组v ( v i < i ) v(v_i<i)v(vi<i)v i v_ivi表示a 1 ∼ a i − 1 a_1\sim a_{i-1}a1ai1中小于a i a_iai的数字的数量
,如果v i = − 1 v_i=-1vi=1,说明这个地方的结果丢失,可以是任意的值,问有多少种可能的排列,在k kk次交换后,可以得到数组v vv,答案对998244353 998244353998244353取模。
思路:
首先,对于任意一种构造的v vv,只要能够满足v i < i v_i<ivi<i,都有且只有一种排列能与之对应。
也就是说,只要我们能够任意构造出来不同的v vv,就会有与之对应的排列能够匹配,那么这样的构造有n ! n!n!种,但是其中有没有不合法的情况呢?
观察这种操作发现这个操作其实是冒泡排序中的一个回合,在操作完k kk个回合后,可以保证最后的k kk个数字一定已经按序归位,且是排列中最大的k kk个数字,在这种情况下v n − k + 1 ∼ v n v_{n-k+1}\sim v_nvnk+1vn的值只能是0 00,所以刚才的构造不够合理。
只要有v n − k + 1 ∼ v n v_{n-k+1}\sim v_nvnk+1vn的值不为0 00− 1 -11,此序列一定无解。
在这里插入图片描述
此外还可以发现,每次操作一次,都会使所有的v i = m a x ( 0 , v i − 1 ) v_i=max(0,v_i-1)vi=max(0,vi1),然后左移一格,将第一格直接覆盖掉,将v n = 0 v_n=0vn=0
考虑每个格子有多少种情况,总乘积就是我们的答案
操作k kk次,就会直接覆盖掉前k kk格,所以前k kk格不管填什么都没有影响,贡献是k ! k!k!
接下来是后面的格子,定义V VV为排列的v vvv i = m a x ( V i + k − k , 0 ) v_i=max(V_{i+k}-k,0)vi=max(Vi+kk,0)
v i v_ivi大于0 00,则我们确定V i + k = v i + k V_{i+k}=v_i+kVi+k=vi+k结果唯一
v i = 0 v_i=0vi=0,则V i + k V_{i+k}Vi+k可以是0 ∼ k 0\sim k0k贡献为k + 1 k+1k+1
v i = − 1 v_i=-1vi=1,无法判断,我们只要让v i < i v_i<ivi<i即可,贡献为i ii

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
int a[maxn];
ll mod=998244353;
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,k;scanf("%d%d",&n,&k);
		int ok=1;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(i>=n-k+1 && a[i]>0) ok=0;
		}
		if(!ok)
		{
			puts("0");continue;
		}
		ll ans=1;
		for(int i=k+1;i<=n;i++)
		{
			if(a[i-k]==0) ans=(ans*(k+1))%mod;
			else if(a[i-k]==-1) ans=(ans*i)%mod;
		}
		for(int i=1;i<=k;i++) ans*=i,ans%=mod;
		printf("%lld\n",ans);
	}
}

版权声明:本文为zppbugmaker原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。