正题
题目链接:https://www.luogu.com.cn/problem/CF1677D
题目大意
对于一个排列pip_ipi,定义一个序列v=F(p)v=F(p)v=F(p),其中vi=∑j=1i−1[pj>pi]v_i=\sum_{j=1}^{i-1}[p_j>p_i]vi=∑j=1i−1[pj>pi]。
一次冒泡排序为依次对1∼n−11\sim n-11∼n−1进行:若pi>pi+1p_i>p_{i+1}pi>pi+1则交换pi,pi+1p_i,p_{i+1}pi,pi+1。
现在给出一个序列vvv,其中有的位置可以任意填数,求有多少个排列ppp进行kkk次冒泡排序后的p′p'p′满足F(p′)=vF(p')=vF(p′)=v。
1≤n≤106,0≤k≤n−11\leq n\leq 10^6,0\leq k\leq n-11≤n≤106,0≤k≤n−1
解题思路
首先排列ppp和F(p)F(p)F(p)之间是能一一对应的。
然后我们考虑把ppp的变化反映到F(p)F(p)F(p)上。
记F(p)=vF(p)=vF(p)=v,那所有会被带着移动的pip_ipi都有vi=0v_i=0vi=0,然后被移动过去的每一个前面比他大的个数少一个。也就是vvv中所有的000都移动到它的下一个000处(如果没有就移动到末尾),然后其余的所有数字减一。
那么也就是原来值在[1,k][1,k][1,k]范围内的冒泡排序kkk次后都变为了000,并且如果一个数字原本是iii变为了000,那么就代表了有iii个000从它前面到达了它的后面,反过来说数字的变化可以代表000的移动。
而且我们还能发现一个性质,就是进行了kkk次冒泡排序后的序列vvv后kkk个值肯定为000。
那么再往前的vi=0v_i=0vi=0的位置在原本就可以是[0,k][0,k][0,k]之间的任意数字了,而且这也决定了后面000的移动,不需要考虑位置关系。
时间复杂度:O(n)O(n)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;
}