牛客练习赛71 C数学考试(思维 + dp)

链接:https://ac.nowcoder.com/acm/contest/7745/C
来源:牛客网
 

数学考试

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

牛牛在树剖姐姐的数学考试里出了一个题,但是树剖姐姐不会做,于是她向您求助。

求 1∼n1\sim n1∼n 的排列,有 m 个限制条件,第i个限制条件 pip_ipi​ 表示前 pip_ipi​ 个数不能是 1∼pi1\sim p_i1∼pi​ 的排列,求符合要求的排列的个数。

答案对 20000311 取模。
 

输入描述:

第一行两个 n,m
第二行 m 个数,每两个数之间用一个空格隔开,第i个数是pi,保证pi互不相同

输出描述:

一个数,表示符合要求的排列个数对 20000311 取模的结果

示例1

输入

4 0

输出

24

示例2

输入

3 2
2 1

输出

3

说明

3 1 2,3 2 1,2 3 1三种合法

示例3

输入

4 2
1 3

输出

14

示例4

输入

114 5
14 1 91 98 10

输出

19843522

(f[i]为i!)用dp[i]表示前 i - 1 个条件满足、第 i 个条件满足的方案数,它等于所有排列 p[i]! 减去不合法的情况,计算不合法的情况时枚举j,dp[j]即为第 j 个条件不满足、前 j - 1个条件满足,所以 dp[i] = p[i]! - dp[j] * f[p[i] - p[j]],f[p[i] - p[j]]为p[j]~p[i]全排,即这一部分对整个1~p[i]的合法性无关了,可以随便排。

扫描二维码关注公众号,回复: 11923884 查看本文章
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 20000311;
const int inf = 0x3f3f3f3f;
const int N = 2e3 + 7;

ll p[N], dp[N], f[N];

void init() {
    f[0] = 1;
    for(ll i = 1; i < N; ++i)
        f[i] = f[i - 1] * i % mod;
}

int main() {
    init();
    ll n, m;
    scanf("%lld%lld", &n, &m);
    for(ll i = 1; i <= m; ++i) {
        scanf("%lld", &p[i]);
    }
    p[m + 1] = n;
    sort(p + 1, p + m + 1);
    for(ll i = 1; i <= m + 1; ++i) {
        dp[i] = f[p[i]];
        for(ll j = 1; j < i; ++j)
            dp[i] = (dp[i] - dp[j] * f[p[i] - p[j]] % mod + mod) % mod;
    }
    printf("%lld\n", dp[m + 1] % mod);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43871207/article/details/108994296