【组合数计数分类/dp】C - Insertion Sort 2018沈阳区域赛

题面

https://vjudge.net/problem/Gym-101955C

题意

有1-n的序列,给一个k,k可以使得n个数的排列里面的前k个数有序,问最终可以得到一个最长上升子序列的长度大于等于n-1的这个序列的排列有多少种可能

题意转化

前k个可以排序,也就是说我们可以把整个序列分成两段,分为【1,k】和【k-1,n】进行分类讨论

思路

一、分类讨论

  1. 后面一段区间完全按照【k-1,n】升序来排列,前面的k个数可以随便排序
    式子:
    k ! k! k!
  2. 前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![(nk1)(nk)(nk1)]

  1. 因为第k+1个位置比较特殊,所以单独考虑,把k+1放到前面区间有k个位置可以选择,而被挤出来的这个数有(n-k)个位置可以放,而前面的区间里还有k个数可以全排列,所以式子为
    ( n − k ) ∗ k ∗ k ! (n-k)*k*k! (nk)kk!

4.把后面(n-k-1)个位置放到前面来,由于有一个大的数字要放到前面去,要满足最长递增子序列的长度大于等于n-1,其他的数只能顺势往后延,没有别的选择,所以式子为
( n − k − 1 ) ∗ k ! (n-k-1)*k! nk1k!
二、最终式子
k ! ∗ ( ( n − k − 1 ) 2 + n − k + k ∗ ( n − k ) ) k!*((n-k-1)^{2}+n-k+k*(n-k)) k!(nk12+nk+k(nk))

代码

#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思路

先欠着…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值