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

[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;
}
posted @ 2026-10-05 21:51  farenara_sayowell  阅读(16)  评论(0)    收藏  举报