专栏文章
题解:P14364 [CSP-S 2025] 员工招聘 / employ(暂无数据)
P14364题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @minfhtci
- 此快照首次捕获于
- 2025/12/02 01:33 3 个月前
- 此快照最后确认于
- 2025/12/02 01:33 3 个月前
按照天的顺序加人。以下定义一个人未被录用为,该人主动放弃或者因为 被拒绝。
当进行完 天时,如果参与完面试的人已经有 个未录用,那么后面所有 的人一定不会被录用, 的人有可能被录用。所以对于每一个 和所有 ,定义 的是有用人, 的是无用人。
设每个 对应的有用人数量 。
设计 dp 状态 表示进行完 天面试,参与完面试的人已经有 个未被录用,且所有参与完面试的 个人中当前的有用人数量为 时,只选择无用人的方案数。相当于有 个空位留给有用人,具体是哪个人待定,先把无用人选了。
考虑从 如何转移到 。
第 天如果选一个对于 来说的无用人参加面试,那么完成后该人一定不会被录用, 。这个时候,原本那些 的人就从有用人变成了无用人,而这样的人有 个。那么我们枚举有 个 的人,在 的状态中被作为有用人参与面试,现在选择这些人之前留的填入空位。因为第 天录用的是对于 无用的人,所以对于 也一定不会有用,不影响 的从有用变无用的转移。可以选的无用人有 个。
第 天如果选择一个有用人,那么 的时候他会被录用,否则不会被录用。
时, 不变,不会使得有用人变成无用人,并且添加一个有用人的时候只需留出位置不需计算方案数,那么转移是简单的。
时,。这个时候,应该先增加一个给 对应的有用人的空位,即 。然后再考虑 的有用人变为无用人的时候,有多少个是属于 的,使用和无用人的转移一样的方法,枚举有 个 的人在 和当前 选择的这个人的状态中被作为有用人参与面试。
最后统计答案,对于每个 ,由于每个人都参与过一次了,所以 。这些未分配方案数的人任意排列即可。
以上各条转移的取值范围,应该满足任何时刻任何变量(包括各种系数、下标、中间变量)都 ,并且 。
虽然有 四层循环,但是由于 ,所以 和 两个循环实际上只会计算 次,因此时间复杂度是 的。
这也算是比较典的 dp 套路了。状态存不下且相互之间暂时没区别的东西,可以先留空,等到后面再算贡献。
注意事项
,有取模阶乘组合数,1s 可能过不去。所以应该所有的循环都只在合法范围内做,并且只转移非 0 状态,这样常数极小,跑的飞快。
直接开 个 long long 类型会爆 512MB 内存限制,所以需要滚动数组优化空间。不知道有没有人因为这个 。
代码
CPP#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define fi first
#define se second
#define pb push_back
#define pii pair<ll,ll>
#define ve vector<ll>
#define mid ((l+r)>>1)
#define lx (x<<1)
#define rx (x<<1|1)
using namespace std;
const ll N=502,P=998244353;
ll n,m,ans,d[N],fc[N],c[N][N],f[2][N][N];
string s;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>s,s=" "+s;
for(ll i=1,x;i<=n;i++)cin>>x,d[x]++;
for(ll i=n;i>=0;i--)d[i]+=d[i+1];
f[0][0][0]=1;
for(ll i=0;i<=n;i++){
fc[i]=(i?fc[i-1]*i:1)%P;
for(ll j=0;j<=i;j++)
c[i][j]=(j?c[i-1][j]+c[i-1][j-1]:1)%P;
}
for(ll i=1;i<=n;i++){
memset(f[i&1],0,sizeof(f[i&1]));
for(ll j=0;j<i;j++)
for(ll k=0;k<=min(i-1,d[j+1]);k++){
ll t=i&1,x=f[!t][j][k];
if(!x)continue;
if(k<d[j+1]){
if(s[i]=='1')(f[t][j][k+1]+=x)%=P;
else for(ll y=max(k+1-d[j+2],0ll);y<=min(d[j+1]-d[j+2],k+1);y++)
(f[t][j+1][k+1-y]+=x*c[d[j+1]-d[j+2]][y]%P*c[k+1][y]%P*fc[y])%=P;
}
if(i-1-k<d[0]-d[j+1])
for(ll y=max(k-d[j+2],0ll);y<=min(d[j+1]-d[j+2],k);y++)
(f[t][j+1][k-y]+=x*(d[0]-d[j+1]-(i-1-k))%P*c[d[j+1]-d[j+2]][y]%P*c[k][y]%P*fc[y])%=P;
}
}
for(ll i=0;i<=n-m;i++)(ans+=f[n&1][i][d[i+1]]*fc[d[i+1]])%=P;
cout<<(ans+P)%P;
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...