[CSP-S 2025] 员工招聘 题解
记录容斥做法,延迟贡献做法先鸽了。
令 \(cnt_i\) 表示 \(c_x \le i\) 的 \(x\) 个数。
设 \(f_{i,j,k}\) 表示前 \(i\) 天已经过完,有 \(j\) 个人没有录取,钦定了 \(k\) 个人是耐心不足的。
当 \(s_i=0\) 时,这天的人必然不被录用,也不用管他是不是耐心不足,所以 \(f_{i+1,j+1,k} \leftarrow f_{i,j,k}\)。
当 \(s_i=1\) 时,可以选择不录用他,即将他钦定为耐心不足,\(f_{i+1,j+1,k+1} \leftarrow (cnt_j-k)\times f_{i,j,k}\)。
也可以选择录用他,但是录用的条件是他必须耐心足够,很麻烦,所以容斥成任意的人减去耐心不足的人的方案。先令 \(f_{i+1,j,k} \leftarrow f_{i,j,k}\),再令 \(f_{i+1,j,k+1}\) 减去 \((cnt_j-k)\times f_{i,j,k}\),即固定了这个人的身份。
\(ans=\sum_{j=0}^{n-m}\sum_{k=0}^n f_{n,j,k}\times (n-k)!\),表示剩下的不确定身份的可以随便填。
为什么这个容斥是对的?假设这是最后一步,则 \(f_{n,j,k}\) 有 \((n-k)!\times f_{n-1,j,k}\) 的贡献,而 \(f_{n,j,k+1}\) 有 \(-(n-k-1)!\times (cnt_j-k)\times f_{n-1,j,k}\) 的贡献,相加之后就是 \(f_{n-1,j,k}\times (n-k-1)!\times (n-cnt_j)\),表示符合录用条件的人的总数 \(n-cnt_j\) 和剩余的人全部自由排列的方案数。
时间复杂度 \(O(n^3)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = 510,mod=998244353;
int c[N],h[N];
string s;
inline void add(int &x,int y){
x=(x+y>=mod)?x+y-mod:x+y;
}
int cnt[N],f[N][N][N],fac[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
cin>>s;s=' '+s;
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=(fac[i-1]*i)%mod;
}
for(int i=1;i<=n;i++){
cin>>c[i];
cnt[c[i]]++;
h[i]=s[i]-'0';
}
for(int i=1;i<=n;i++){
cnt[i]+=cnt[i-1];
}
f[0][0][0]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
for(int k=0;k<=i;k++){
if(h[i+1]==0){
add(f[i+1][j+1][k],f[i][j][k]);
}else{
add(f[i+1][j+1][k+1],(cnt[j]-k)*f[i][j][k]%mod);
add(f[i+1][j][k],f[i][j][k]);
add(f[i+1][j][k+1],(mod-(cnt[j]-k)*f[i][j][k]%mod)%mod);
}
}
}
}
int ans=0;
for(int j=0;j<=n-m;j++){
for(int k=0;k<=n;k++){
add(ans,f[n][j][k]*fac[n-k]%mod);
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号