题面
https://vjudge.net/problem/Gym-101955C
题意
有1-n的序列,给一个k,k可以使得n个数的排列里面的前k个数有序,问最终可以得到一个最长上升子序列的长度大于等于n-1的这个序列的排列有多少种可能
题意转化
前k个可以排序,也就是说我们可以把整个序列分成两段,分为【1,k】和【k-1,n】进行分类讨论
思路
一、分类讨论
- 后面一段区间完全按照【k-1,n】升序来排列,前面的k个数可以随便排序
式子:
k ! k! k! - 前k个不管,k!,后面有一个数可以乱序,就是一个(n-k)个数可以选择,可以放在除自己位置的其他(n-k-1)位置,但是会有重复的时候,重复的情况是这样的:
我们会发现4和5之间有一个重复,5和6之间有一个重复,所以只要在原来基础上减去(n-k-1)
k ! ∗ [ ( n − k − 1 ) ∗ ( n − k ) − ( n − k − 1 ) ] k!*[(n-k-1)*(n-k)-(n-k-1)] k!∗[(n−k−1)∗(n−k)−(n−k−1)]
- 因为第k+1个位置比较特殊,所以单独考虑,把k+1放到前面区间有k个位置可以选择,而被挤出来的这个数有(n-k)个位置可以放,而前面的区间里还有k个数可以全排列,所以式子为
( n − k ) ∗ k ∗ k ! (n-k)*k*k! (n−k)∗k∗k!
4.把后面(n-k-1)个位置放到前面来,由于有一个大的数字要放到前面去,要满足最长递增子序列的长度大于等于n-1,其他的数只能顺势往后延,没有别的选择,所以式子为
(
n
−
k
−
1
)
∗
k
!
(n-k-1)*k!
(n−k−1)∗k!
二、最终式子
k
!
∗
(
(
n
−
k
−
1
)
2
+
n
−
k
+
k
∗
(
n
−
k
)
)
k!*((n-k-1)^{2}+n-k+k*(n-k))
k!∗((n−k−1)2+n−k+k∗(n−k))
代码
#include<iostream>
using namespace std;
typedef long long ll;
int t,n,k,q;
ll cal(int x)
{
ll res=1;
for(int i=2;i<=x;i++)
res=(res*i)%q;
return res;
}
int main()
{
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
scanf("%d%d%d",&n,&k,&q);
ll ans=0;
if(k>=n-1) k=n;//k有大于n的情况,所以要判一下
ll jc=cal(k);
ans=(((ll)(n-k-1)*(n-k-1)%q+(n-k)%q)%q+(ll)k*(n-k)%q)%q;
ans=ans*jc%q;
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}
dp思路
先欠着…