专栏文章

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

P14364题解参与者 4已保存评论 4

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
4 条
当前快照
1 份
快照标识符
@minfax1r
此快照首次捕获于
2025/12/02 01:28
3 个月前
此快照最后确认于
2025/12/02 01:28
3 个月前
查看原文
观察数据范围发现可以 O(n3)O(n^3),考虑dp。
dpi,j,kdp_{i,j,k} 表示已经过了 ii 天,已经有 jj 个人被拒绝或放弃,前 ii 天中有 kk 个人满足 ckjc_k\le j 时的方案数,不考虑 ci>jc_i> j 的人之间的差别
cnticnt_i 表示,preipre_i 表示其前缀和。
考虑从 dpi,j,kdp_{i,j,k} 转移:
  • si+1=0s_{i+1}=0
    显然要转移到 dpi+1,j+1,kdp_{i+1,j+1,k^\prime}kk^\prime 的范围有多个限制,详见代码)
    先不考虑第 i+1i+1 天的人,由于之前没有区分 ci=j+1c_i=j+1 的人和 ci>j+1c_i> j+1 的人,现在需要另外计算这部分的贡献,即 new=dpi,j,k×(ikkk)(cntj+1kk)(kk)!new=dp_{i,j,k}\times \begin{pmatrix} i-k \\ k^\prime-k \end{pmatrix}\begin{pmatrix} cnt_{j+1} \\ k^\prime-k \end{pmatrix}(k^\prime-k)!(如果dp状态时考虑了所有人的差别,这里就不好转移),然后根据第 i+1i+1 天的人分类讨论:
    • j+1\le j+1dpi+1,j+1,k+1new×(prej+1k)dp_{i+1,j+1,k^\prime+1}\leftarrow new\times (pre_{j+1}-k^\prime)
    • >j+1> j+1dpi+1,j+1,knewdp_{i+1,j+1,k^\prime}\leftarrow new
  • si+1=1s_{i+1}=1
    这次先分类讨论:
    • >j> j,直接 dpi+1,j,kdpi,j,kdp_{i+1,j,k}\leftarrow dp_{i,j,k}
    • j\le j,和上面一样处理后 dpi+1,j+1,k+1new×(prejk)dp_{i+1,j+1,k'+1}\leftarrow new\times (pre_j-k)
注意计算答案时要算上未区分的人的贡献。
时间复杂度:
  • 转移复杂度 O(cntj+1)O(cnt_{j+1}),而 cntj=n\sum cnt_j=n,所以总复杂度 O(n3)O(n^3),可以通过本题。
空间用类似01背包的方式去掉一维即可。

代码

有一些边界情况要注意
CPP
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353,N=505;
long long f[N],g[N],inv[N];
int n,m,cnt[N],pre[N],dp[N][N];
char s[N];

int main()
{
    scanf("%d %d",&n,&m);
    f[0]=f[1]=g[0]=g[1]=inv[1]=1;
    for(int i=2;i<=n;i++)
    {
        f[i]=f[i-1]*i%mod;
        inv[i]=inv[mod%i]*(mod-mod/i)%mod;
        g[i]=g[i-1]*inv[i]%mod;
    }
    scanf("%s",s+1);
    for(int i=1,c;i<=n;i++)
    {
        scanf("%d",&c);
        cnt[c]++;
    }
    pre[0]=cnt[0];
    for(int i=1;i<=n;i++)pre[i]=pre[i-1]+cnt[i];
    dp[0][0]=1;
    for(int i=0;i<n;i++)
    {
        for(int j=i;j>=0;j--)
        {
            for(int k=pre[j];k>=0;k--)
            {
                int now=dp[j][k];
                dp[j][k]=0;
                if(s[i+1]=='0')
                {
                    for(int k2=max(k,i-(n-pre[j+1]));k2<=k+cnt[j+1]&&k2<=i;k2++)
                    {
                        const int tot=i-k,ths=k2-k,num=cnt[j+1];
                        const long long x=f[tot]*g[ths]%mod*g[tot-ths]%mod*f[num]%mod*g[num-ths]%mod;
                        if(j+1!=n&&pre[j+1]-k2!=n-i)dp[j+1][k2]=(dp[j+1][k2]+x*now)%mod;
                        dp[j+1][k2+1]=(dp[j+1][k2+1]+x*now%mod*(pre[j+1]-k2))%mod;
                    }
                }
                else
                {
                    for(int k2=max(k,i-(n-pre[j+1]));k2<=k+cnt[j+1]&&k2<=i;k2++)
                    {
                        const int tot=i-k,ths=k2-k,num=cnt[j+1];
                        const long long x=f[tot]*g[ths]%mod*g[tot-ths]%mod*f[num]%mod*g[num-ths]%mod;
                        dp[j+1][k2+1]=(dp[j+1][k2+1]+x*now%mod*(pre[j]-k))%mod;
                    }
                    if(pre[j]-k!=n-i)dp[j][k]=now;
                }
            }
        }
    }
    int ans=0;
    for(int j=0;j<=n-m;j++)
    {
        const int k=pre[j];
        ans=(ans+dp[j][k]*f[n-k])%mod;
    }
    printf("%d",ans);
    return 0;
}

评论

4 条评论,欢迎与作者交流。

正在加载评论...