CF1677D-Tokitsukaze and Permutations【结论】

正题

题目链接:https://www.luogu.com.cn/problem/CF1677D


题目大意

对于一个排列\(p_i\),定义一个序列\(v=F(p)\),其中\(v_i=\sum_{j=1}^{i-1}[p_j>p_i]\)

一次冒泡排序为依次对\(1\sim n-1\)进行:若\(p_i>p_{i+1}\)则交换\(p_i,p_{i+1}\)

现在给出一个序列\(v\),其中有的位置可以任意填数,求有多少个排列\(p\)进行\(k\)次冒泡排序后的\(p'\)满足\(F(p')=v\)

\(1\leq n\leq 10^6,0\leq k\leq n-1\)


解题思路

首先排列\(p\)\(F(p)\)之间是能一一对应的。

然后我们考虑把\(p\)的变化反映到\(F(p)\)上。

\(F(p)=v\),那所有会被带着移动的\(p_i\)都有\(v_i=0\),然后被移动过去的每一个前面比他大的个数少一个。也就是\(v\)中所有的\(0\)都移动到它的下一个\(0\)处(如果没有就移动到末尾),然后其余的所有数字减一。

那么也就是原来值在\([1,k]\)范围内的冒泡排序\(k\)次后都变为了\(0\),并且如果一个数字原本是\(i\)变为了\(0\),那么就代表了有\(i\)\(0\)从它前面到达了它的后面,反过来说数字的变化可以代表\(0\)的移动。

而且我们还能发现一个性质,就是进行了\(k\)次冒泡排序后的序列\(v\)\(k\)个值肯定为\(0\)

那么再往前的\(v_i=0\)的位置在原本就可以是\([0,k]\)之间的任意数字了,而且这也决定了后面\(0\)的移动,不需要考虑位置关系。

时间复杂度:\(O(n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e6+10,P=998244353;
ll T,n,k,a[N];
signed main()
{
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld",&n,&k);
		for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
		bool flag=0;
		for(ll i=n;i>n-k;i--)
			if(a[i]>0){flag=1;break;}
		if(flag){puts("0");continue;}
		ll ans=1;
		for(ll i=1;i<=n-k;i++)
			if(a[i]==0)ans=ans*(k+1)%P;
			else if(a[i]==-1) ans=ans*(i+k)%P;
		for(ll i=1;i<=k;i++)ans=ans*i%P;
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2022-06-04 15:30  QuantAsk  阅读(38)  评论(0编辑  收藏  举报