P14364题解

P14364 [CSP-S 2025] 员工招聘 (employ)

闲话

去年这题基本没看懂(我去年这么这么唐,完全没开智来的),今年一看,感觉不算太难。
trick:容斥思想优化 dp。

题目大意

有 \(n\) 个人,给出可以录用的时间 \(s\),每个人只有在满足自己在被录用的时间,且自己的 \(c_i > x\),其中 \(x\) 表示之前未录用人数,求至少录用 \(m\) 个人的排列顺序有多少种。

题目分析

考虑 dp。

那么就有两种情况:

  1. 当前位置 \(s_i=0\),那么我们这里随便放人都可以
  2. 否则,会有录取与不录取两张情况:
  1. 录取,\(c_i > x\)
  2. 不录取,\(c_i \le x\)

我们发现 \(s_i=1\) 这两种情况都是 \(c_i\) 与 \(x\) 的关系,我们可以根据 \([c_i > x]=1-[c_i \le x]\) 容斥掉一种情况,这样我们就可以只处理一种情况了。考虑容斥掉 \(c_i > x\)。

设计状态为 \(f_{i,j,k}\),分别为当前位置、已确认不录取人数、已钦定状态人数。

代码

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=500+5;
const int P=998244353;
int n,m;
string s;
int a[N];
int f[N][N][N],fac[N];
int main()
{
    cin>>n>>m;
    fac[0]=1;
    for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%P;
    cin>>s;
    s=' '+s;
    for(int i=1;i<=n;i++)
    {
        int x;cin>>x;
        a[x]++;
    }
    for(int i=1;i<=n;i++)a[i]=a[i]+a[i-1];
    f[0][0][0]=1;//初始状态
    for(int i=0;i<n;i++)
    {
        if(s[i+1]=='0')
            for(int j=0;j<=i;j++)
                for(int k=0;k<=i;k++)
                    (f[i+1][j+1][k]+=f[i][j][k])%=P;//可以随便放,直接 j+1 就好了
        else
            for(int j=0;j<=i;j++)
                for(int k=0;k<=i;k++)
                {
                    (f[i+1][j][k]+=f[i][j][k])%=P;//容斥时,因为拆掉了录取的状态,此时我们先把没有钦定影响的方案数加上
                    (f[i+1][j][k+1]-=1ll*(a[j]-k)*f[i][j][k]%P-P)%=P;//录取时,会钦定一个数,因为容斥系数,我们减掉
                    (f[i+1][j+1][k+1]+=1ll*(a[j]-k)*f[i][j][k]%P)%=P;//不录取时,也会钦定一个数,同理我们加上
                }
    }
    long long ans=0;
    for(int j=0;j<=n-m;j++)for(int k=0;k<=n;k++)ans=(ans+1ll*f[n][j][k]*fac[n-k]%P)%P;
    cout<<ans;
}
posted @ 2026-10-29 21:04  tidewave  阅读(5)  评论(0)    收藏  举报