Gym-101955C Insertion Sort(组合数学)

Gym-101955C Insertion Sort(组合数学)

题意

给一个只排前k项的插入排序算法,问一个以1-n为元素的数组有多少种排列方式能在这个前k项排序算法完成之后能使最长上升子序列的长度达到n-1。

思路

组合数学推公式的题目。首先我们发现前k个数字是会被排序的,所以前k个数字没有顺序要求,只要是1-k的话,随便怎么排都行,一个全排列就是阶乘k。前面都是1-k的情况下,后面n-k个数字可以把其中一个数字任意插入其他n-k-1个数字之间,这个公式胖清推出来了:

( n − k ) 2 + 2 × ( n − k − 1 ) (n-k)^2+2\times(n-k-1) (nk)2+2×(nk1)

除此之外我们也能从前k个数里面取一个数去和后面的数字交换。任意从前k个数取一个数记为x,则x可以和k+1交换,这样只有x是无序的,可以插入后面任意的位置,后面其他数保持有序即可。这样的插入有:

k × ( n − k ) k\times(n-k) k×(nk)

即有n-k种插入方式和k种交换方式(前k个数和k+1换),乘法原理。

k这个数比较特殊,它除了k+1可以换以外,还能和后面其他数字(我们记为y)交换,交换完以后只能呆在k+1的前方,这样无序的数就是y了,这种方式有:

n − k − 1 n-k-1 nk1

即有和n-k-1个数字交换的方式,但是只有一种插入方式(k+1前方)。

这三种摆放方式是并列的,所以加法原理加起来,然后乘上前面k个数字的全排列即可。

完整的公式为:

( ( n − k ) 2 + 2 × ( n − k − 1 ) + k × ( n − k ) + n − k − 1 ) × k ! ((n-k)^2+2\times(n-k-1)+k\times(n-k)+n-k-1)\times k! ((nk)2+2×(nk1)+k×(nk)+nk1)×k!

注意一下取模,公式方面就没有问题了。但是!但是!!!

k不一定小于n!!!k不一定小于n!!!k不一定小于n!!!

就是这个坑让我们自闭了两个小时,怎么推公式都是对的,自己写个函数检查都是对的,各种重算,结果看了题解就是这么个坑???一改就对了???

仿佛秦皇岛站没有看懂题意特判的事情又重现了。沈阳败了。

代码

#include
using namespace std;
#define ll long long
const int maxn=50;
ll fac[maxn+5];
void init(ll mod){
    fac[0]=1;
    for(int i=1;i<=maxn;i++){
        fac[i]=fac[i-1]*i%mod;//打阶乘表
    }
}
ll get(ll k,ll mod){//前k个数字不换的情况,模块化编程,胖清提供
    ll ans=k*k%mod;
    ans=(ans-2*(k-1)+mod)%mod;
    return ans;
}
void solve(int ca){
    ll n,k,q;
    scanf("%lld%lld%lld",&n,&k,&q);
    init(q);
    if (n<=k){//如果k大于等于n,等于说是全都排好序了,那你怎么排都行
        printf("Case #%d: %lld\n",ca,fac[n]);
        return;
    }
    ll t=get(n-k,q);
    ll ans=(k*(n-k)%q+n-k-1+t)%q*fac[k]%q;//剩余的公式,阿忠哥和我推的
    printf("Case #%d: %lld\n",ca,ans);
}
int main(){
//freopen("test.in","r",stdin);
	int t;
	scanf("%d",&t);
	int ca=0;
	while (t--)
        solve(++ca);
	return 0;
}

参考了以下文章,在此表示感谢:

https://www.cnblogs.com/fan-jiaming/p/10047017.html

你可能感兴趣的:(2018年算法训练)