专栏文章
题解:P14364 [CSP-S 2025] 员工招聘 / employ(民间数据)
P14364题解参与者 3已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @minf2h8f
- 此快照首次捕获于
- 2025/12/02 01:22 3 个月前
- 此快照最后确认于
- 2025/12/02 01:22 3 个月前
前言
差点场切,写篇题解纪念一下。
Upd:修了一些锅。
思路
首先我们发现,若最后有 个人没有选上,那么 的所有位置可以随便乱排,也就是说我们只需要关心 的位置。
所以就可以设状态 表示前 天, 个人没选上,有 个位置满足 。
设 表示 的个数, 表示 的和。转移考虑分类讨论:
- :
- 若当前这一位选上,填的数一定大于 ,所以 ;
- 否则, 就会加一,填的数也一定小于等于 ,所以 也要加一,此时我们需要枚举前 天中等于 的个数 ,那么转移即为 。
- :无论如何一定不会选上,我们只需要考虑填的数和 的关系,以及前 天中等于 的个数 。
- 小于等于 ,转移为 ;
- 否则,转移为 。
考虑如何计算答案,发现最后我们只需要让 ,然后在乘上剩下的数的填法,即 。
分析一下复杂度,由于 一定小于等于 ,总和是 的,所以时间复杂度可以被分析到 。
代码
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 505,mod = 998244353;
int n,m,ton[N],pre[N],a[N][N],c[N][N],f[2][N][N],fac[N];
string s;
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i = 1,x;i<=n;i++)
cin>>x,ton[x]++;
pre[0] = ton[0];
for(int i = 1;i<=n;i++)
pre[i] = pre[i-1]+ton[i];
c[0][0] = 1;
for(int i = 1;i<=n;i++)
{
c[i][0] = 1;
for(int j = 1;j<=i;j++)
c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod;
}
a[0][0] = 1;
for(int i = 1;i<=n;i++)
{
int now = 1;
for(int j = 0;j<=i;j++)
{
a[i][j] = now;
now = now*(i-j)%mod;
}
}
fac[0] = 1;
for(int i = 1;i<=n;i++)
fac[i] = fac[i-1]*i%mod;
f[0][0][0] = 1;
for(int i = 0;i<n;i++)
{
int now = (i&1);
for(int j = 0;j<=n;j++)
for(int k = 0;k<=n;k++)
{
if(!f[now][j][k]) continue;
if(s[i]=='1')
{
(f[now^1][j][k]+=f[now][j][k])%=mod;
for(int x = 0;x<=min(i-k,ton[j+1]);x++)
(f[now^1][j+1][k+x+1]+=f[now][j][k]*c[ton[j+1]][x]%mod*a[i-k][x]%mod*(pre[j]-k))%=mod;
}
else
{
for(int x = 0;x<=min(i-k,ton[j+1]);x++)
{
(f[now^1][j+1][k+x]+=f[now][j][k]*c[ton[j+1]][x]%mod*a[i-k][x]%mod)%=mod;
(f[now^1][j+1][k+x+1]+=f[now][j][k]*c[ton[j+1]][x]%mod*a[i-k][x]%mod*(pre[j+1]-k-x))%=mod;
}
}
f[now][j][k] = 0;
}
}
int ans = 0;
for(int i = 0;i<=n-m;i++)
(ans+=f[n&1][i][pre[i]]*fac[n-pre[i]])%=mod;
cout<<ans;
return 0;
}
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...