Insertion Sort(排列组合)[Gym - 101955C]

原题传送门


题面

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)(kn)+(kn1)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值