[CSP-S 2025] 员工招聘题解

P14364 [CSP-S 2025] 员工招聘 / employ(民间数据)

题目背景

由于评测机性能差异,本题时限提升 1 秒。

测试数据未经过检验,可能存在问题,或者可能存在错误做法获得较高分数,仅供参考。

  • 2025.11.02 11:21 民间数据更新:对特殊性质 A 的数据点做了修改。

题目描述

小 Z 和小 H 想要合伙开一家公司,共有 n n n 人前来应聘,编号为 1 ∼ n 1 \sim n 1n。小 Z 和小 H 希望录用至少 m m m 人。

小 H 是面试官,将在接下来 n n n 天每天面试一个人。小 Z 负责决定应聘人前来面试的顺序。具体地,小 Z 可以选择一个 1 ∼ n 1 \sim n 1n 的排列 p p p,然后在第 i i i ( 1 ≤ i ≤ n 1 \leq i \leq n 1in) 天通知编号为 p i p_i pi 的人前来面试。

小 H 准备了 n n n 套难度不一的面试题。由于 n n n 个前来应聘的人水平大致相同,因此对于同一套题,所有人的作答结果是一致的。具体地,第 i i i ( 1 ≤ i ≤ n 1 \leq i \leq n 1in) 天的面试题的难度为 s i ∈ { 0 , 1 } s_i \in \{0,1\} si{0,1},其中 s i = 0 s_i = 0 si=0 表示这套题的难度较高,没有人能够做出; s i = 1 s_i = 1 si=1 表示这套题的难度较低,所有人都能做出。小 H 会根据面试者的作答结果决定是否录用,即如果面试者没有做出面试题,则会拒绝,否则会录用。

然而,每个人的耐心都有一定的上限,如果在他面试之前未录用的人数过多,则他会直接放弃参加面试。具体地,编号为 i i i ( 1 ≤ i ≤ n 1 \leq i \leq n 1in) 的人的耐心上限可以用非负整数 c i c_i ci 描述,若在他之前已经有不少于 c i c_i ci 人被拒绝或放弃参加面试,则他也将放弃参加面试。

小 Z 想知道一共有多少种面试的顺序 p p p 能够让他们录用至少 m m m 人。你需要帮助小 Z 求出,能够录用至少 m m m 人的排列 p p p 的数量。由于答案可能较大,你只需要求出答案对 998   244   353 998\,244\,353 998244353 取模后的结果。

输入格式

输入的第一行包含两个正整数 n , m n, m n,m,分别表示前来应聘的人数和希望录用的人数。

输入的第二行包含一个长度为 n n n 的字符串 s 1 … s n s_1 \dots s_n s1sn,表示每一天的面试题的难度。

输入的第三行包含 n n n 个非负整数 c 1 , c 2 , … , c n c_1, c_2, \dots, c_n c1,c2,,cn,表示每个人的耐心上限。

输出格式

输出一行一个非负整数,表示能够录用至少 m m m 人的排列 p p p 的数量对 998   244   353 998\,244\,353 998244353 取模后的结果。

输入输出样例 #1

输入 #1

3 2
101
1 1 2

输出 #1

2

输入输出样例 #2

输入 #2

10 5
1101111011
6 0 4 2 1 2 5 4 3 3

输出 #2

2204128

说明/提示

【样例 1 解释】

共有以下 2 种面试的顺序 p p p 能够让小 Z 和小 H 录用至少 2 人:

  1. p = [ 1 , 2 , 3 ] p = [1,2,3] p=[1,2,3], 依次录用编号为 1 的人和编号为 3 的人;
  2. p = [ 2 , 1 , 3 ] p = [2,1,3] p=[2,1,3], 依次录用编号为 2 的人和编号为 3 的人。

【样例 3】

见选手目录下的 employ/employ3.in \textbf{\textit{employ/employ3.in}} employ/employ3.in employ/employ3.ans \textbf{\textit{employ/employ3.ans}} employ/employ3.ans

该样例满足测试点 6 ~ 8 的约束条件。

【样例 4】

见选手目录下的 employ/employ4.in \textbf{\textit{employ/employ4.in}} employ/employ4.in employ/employ4.ans \textbf{\textit{employ/employ4.ans}} employ/employ4.ans

该样例满足测试点 12 ~ 14 的约束条件。

【样例 5】

见选手目录下的 employ/employ5.in \textbf{\textit{employ/employ5.in}} employ/employ5.in employ/employ5.ans \textbf{\textit{employ/employ5.ans}} employ/employ5.ans

该样例满足测试点 18 ~ 21 的约束条件。

【数据范围】

对于所有测试数据,保证:

  • 1 ≤ m ≤ n ≤ 500 1 \leq m \leq n \leq 500 1mn500;
  • 对于所有 1 ≤ i ≤ n 1 \leq i \leq n 1in,均有 s i ∈ { 0 , 1 } s_i \in \{0,1\} si{0,1};
  • 对于所有 1 ≤ i ≤ n 1 \leq i \leq n 1in,均有 0 ≤ c i ≤ n 0 \leq c_i \leq n 0cin

::cute-table{tuack}

测试点编号 n ≤ n \leq n m m m特殊性质
1 , 2 1,2 1,2 10 10 10 ≤ n \leq n n
3 ∼ 5 3 \sim 5 35 18 18 18^^
6 ∼ 8 6 \sim 8 68 1 0 2 10^2 102^A
9 ∼ 11 9 \sim 11 911^^
12 ∼ 14 12 \sim 14 1214 500 500 500 = 1 =1 =1^
15 15 15^ = n =n =n^
16 , 17 16,17 16,17^ ≤ n \leq n nA
18 ∼ 21 18 \sim 21 1821^^B
22 ∼ 25 22 \sim 25 2225^^

特殊性质 A: 对于所有 1 ≤ i ≤ n 1 \leq i \leq n 1in,均有 s i = 1 s_i = 1 si=1

特殊性质 B: 在 s 1 , s 2 , … , s n s_1, s_2, \dots, s_n s1,s2,,sn 中最多只有 18 个取值为 1,即 ∑ i = 1 n s i ≤ 18 \sum_{i=1}^{n} s_i \leq 18 i=1nsi18

思路

DP即可,首先容易想到暴力状态压缩DP。

暴力代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,c[100005],mod=998244353,f[20][300005],rm[200005],d,df=0,op=0;
char s[100005];
bool bo[100005];
int main(){
	cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>s[i];
    }
    for(int i=1;i<=n;i++){
        cin>>c[i];
    }
    rm[0]=1;
    for(int i=1;i<=18;i++){
        rm[i]=rm[i-1]*2;
    }
    if(n<=18){
        f[0][0]=1;
        d=pow(2,n)-1;
        for(int i=0;i<=n-1;i++){
            for(int j=0,k;j<=d;j++){
                //cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
                k=j;
                df=0;
                for(int o=0;o<=n-1;o++){
                    if(k%2==1){
                        bo[o]=1;
                        df++;
                    }
                    else{
                        bo[o]=0;
                    }
                    k/=2;
                }
                for(int o=0;o<=n-1;o++){
                    if(bo[o]==0){
                        if(s[df+1]=='0'){
                            f[i][j+rm[o]]=(f[i][j+rm[o]]+f[i][j])%mod;
                        }
                        else{
                            if(c[o+1]<=df-i){
                                f[i][j+rm[o]]=(f[i][j+rm[o]]+f[i][j])%mod;
                            }
                            else{
                                f[i+1][j+rm[o]]=(f[i+1][j+rm[o]]+f[i][j])%mod;
                            }
                        }
                    }
                }
            }
        }
        for(int i=m;i<=n;i++){
            op=(op+f[i][d])%mod;
        }
        cout<<op<<endl;
    }
    else{
        cout<<0<<endl;
    }
	return 0;
}

显然过不了题目,所以考虑优化。以 f i , j , k f_{i,j,k} fi,j,k表示至第i人,j个人失败,k个耐心不大于j的人,参照了出题人题解

代码见下

#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
long long n,m,c[100005],f[505][505][505],b[505],d[505],cc[505][505],jx[100005],op=0;
char s[100005];
bool bo[100005];
int main(){
	cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>s[i];
    }
    for(int i=1;i<=n;i++){
        cin>>c[i];
        b[c[i]]++;
    }
    d[0]=b[0];
    for(int i=1;i<=n;i++){
        d[i]=d[i-1]+b[i];
    }
    cc[0][0]=jx[0]=1;
    for(int i=1;i<=n;i++){
        cc[i][0]=1;
        jx[i]=jx[i-1]*i%mod;
        for(int j=1;j<=i;j++){
            cc[i][j]=(cc[i-1][j]+cc[i-1][j-1])%mod;
        }
    }
    f[0][0][0]=1;
    for(int i=0;i<=n-1;i++){
        if(s[i+1]=='1'){
            for(int j=0;j<=i;j++){
                for(int k=0;k<=d[j]&&k<=i;k++){
                    if(n-d[j]>=i-k+1){
                        f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%mod;
                    }
                    for(int o=0;o<=b[j+1]&&o<=i-k;o++){
                        f[i+1][j+1][k+o+1]=(f[i+1][j+1][k+o+1]+f[i][j][k]*(d[j]-k)%mod*cc[b[j+1]][o]%mod*cc[i-k][o]%mod*jx[o]%mod)%mod;
                    }
                }
            }
        }
        else{
            for(int j=0;j<=i;j++){
                for(int k=0;k<=d[j]&&k<=i;k++){
                    for(int o=0;o<=b[j+1]&&o<=i-k;o++){
                        if(n-d[j+1]>=i-k-o+1){
                            f[i+1][j+1][k+o]=(f[i+1][j+1][k+o]+f[i][j][k]%mod*cc[b[j+1]][o]%mod*cc[i-k][o]%mod*jx[o]%mod)%mod;
                        }                        
                        f[i+1][j+1][k+o+1]=(f[i+1][j+1][k+o+1]+f[i][j][k]*(d[j+1]-k-o)%mod*cc[b[j+1]][o]%mod*cc[i-k][o]%mod*jx[o]%mod)%mod;
                    }
                }
            }            
        }
    }
    for(int i=0;i<=n-m;i++){
        op=(op+f[n][i][d[i]]*jx[n-d[i]]%mod)%mod;
    }
    cout<<op<<endl;
	return 0;
}
posted @ 2025-11-03 21:24  bz02_2023f2  阅读(2)  评论(0)    收藏  举报  来源