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