原题传送门
题面
Insertion Sort
time limit per test6 seconds
memory limit per test1024 megabytes
inputstandard input
outputstandard output
problem describe
Insertion sort is a simple sorting algorithm that builds the final sorted array one item at an iteration.
More precisely, insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. It repeats until no input elements remain.
This type of sorting is typically done in-place, by iterating up the array, growing the sorted array behind it. At each array-position, it checks the value there against the largest value in the sorted array (which happens to be next to it, in the previous array-position checked). If larger, it leaves the element in place and moves to the next. If smaller, it finds the correct position within the sorted array, shifts all the larger values up to make a space, and inserts into that correct position.
The resulting array after k iterations has the property where the first k entries are sorted. In each iteration the first remaining entry of the input is removed, and inserted into the result at the correct position, thus extending the result.
Knuth is an ACM-ICPC master and provides a modified pseudocode implementation about the insertion sort for you. His modified algorithm for an array of sortable items A (1-based array) can be expressed as:
He notes that a permutation of 1 to n is almost sorted if the length of its longest increasing subsequence is at least (n−1).
Given the parameter k, you are asked to count the number of distinct permutations of 1 to n meeting the condition that, after his modified insertion sort, each permutation would become an almost sorted permutation.
Input
The input contains several test cases, and the first line contains a positive integer T indicating the number of test cases which is up to 5000.
For each test case, the only line contains three integers n,k and q indicating the length of the permutations, the parameter in his implementation and a prime number required for the output respectively, where 1 ≤ n,k ≤ 50 and 1 0 8 10^8 108 ≤ q ≤ 1 0 9 10^9 109.
Output
For each test case, output a line containing “Case #x: y” (without quotes), where x is the test case number starting from 1, and y is the remainder of the number of permutations which meet the requirement divided by q.
Sample Input
4
4 1 998244353
4 2 998244353
4 3 998244353
4 4 998244353
Sample Output
Case #1: 10
Case #2: 14
Case #3: 24
Case #4: 24
题意描述
对于一个长度为n的序列,求出进行k次插入排序将头k个数字排序后上升子序列长度(不连续)大于等于n-1的序列个数.
题目分析
目标序列都符合上升子序列长度大于等于n-1,所以会存在一个自由移动的数字x
这些序列可以被分成两个部分
前面k个数字+后面剩余的n-k个数字
显然前面的k个数字可以任意排序,通过插入排序恢复有序,对于x数字的来源进行分开讨论
以下讨论首先基于一个长度为n的有序序列
若x来自前面k个数字
当x取出并且重新插入序列后仍然是前k个数字
因为前k个数字会自动排序,所以前面k个数字的贡献是
k
!
k!
k!
只需要考虑后面剩余的n-k个数字,即上升子序列长度为n-k-1的序列个数
当x取出并且重新插入序列后为剩余的n-k个数字,考虑到第k+1个数字的位置会往前到k,为了保证上升子序列的长度为n-1,所以x只能插入在第k+1个数字后面
前k个数字会自动排序,所以贡献同上
因为x插入后成为剩余的n-k个数字中的一个,所以除了x,剩余的n-k-1个数字要保证有序.那么只需要考虑可以取为x的个数和可以插入的空隙个数
若x来自后面剩余的n-k个数字
当x取出并且重新插入序列后为前k个数字,考虑到第k个数字的位置被上升到k+1,而为了保证上升子序列的长度为n-1,所以x只能插入在第k个数字之前
因为前k个数字会自动排序,所以无需考虑x能够插入的位置个数
当x取出并且重新插入序列后为后n-k个数字,这种情况和前述的情况重复了,所以不需要计算入答案中
备注
显然,会发现,若取第k个插入到后面n-k个数字中和取第k+1个数字插入到前面k个数字,并且后面n-k个数字有序的时候会出现重复,所以按照上述所得的计算结果应该减去
k
!
k!
k!
但是也会发现,经过前k个数字的自动排序后得到完全有序的序列的序列个数并没有被计算入内,所以减去重复并且加上遗漏正好抵消
综上所得公式应该为
a
n
s
=
k
!
∗
(
(
k
+
1
)
∗
(
k
−
n
)
+
(
k
−
n
−
1
)
2
)
ans = k!*((k+1)*(k-n)+(k-n-1)^2)
ans=k!∗((k+1)∗(k−n)+(k−n−1)2)
具体代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <stack>
using namespace std;
typedef long long ll;
ll solve(ll k, ll q){
ll sum = 1;
for(ll i = 1; i <= k; i++) sum = sum * i % q;
return sum;
}
int main(){
int t, kase = 0;
scanf("%d", &t);
while(t--){
ll n, k, q, ans;
scanf("%lld%lld%lld", &n, &k, &q);
if(k >= n) k = n;
ans = solve(k, q);
ans = ans * ((k + 1) * (n - k) % q + (n - k - 1) * (n - k - 1) % q) % q;
printf("Case #%d: %lld\n", ++kase, ans);
}
return 0;
}