专栏文章
题解:P14568 【MX-S12-T3】排列
P14568题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min2d30i
- 此快照首次捕获于
- 2025/12/01 19:26 3 个月前
- 此快照最后确认于
- 2025/12/01 19:26 3 个月前
VP 获得 分,怎么回事呢。
永远不要忘记排列 DP 的两种状态维护方法——绝对大小和相对大小。
按值域 DP 没有什么前途,考虑按位置 DP,假设 已确定,注意到 时第 位填法有且仅有一种,且为当前未出现数字中最小/最大的一个,记 表示 中最小值为 最大值为 即可 ,如果只记录最小值可以做到 并通过特殊性质 BC。
考虑特殊性质 A,不难发现答案恰好为 ,原因是每一位与前面数字的相对大小固定,现在我们获得了 分。
我们刚刚设计状态时维护的是前面数字绝对大小的信息,考虑换一个思路,维护相对大小。
容易发现对于任意前缀,把 的位置拿出来,它们的相对大小固定。
考虑此时插入一个 的数字,那么此时后面的数都必须大于这个数,因此相对大小比这个数小的位置就废掉了,这些数的绝对大小在此刻已经确定。
同理,因此令 表示 中有 个数字绝对大小未确定的方案数。
对于 ,。
对于 ,。
但是这样是有漏洞的,如果 出现 或 子序列,我们的 DP 会默认 的位置可以填入数字,实际上是不行的,不难发现当且仅当出现这种情况时无解,直接特判输出 即可。
CPP#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
void upd(ll &x,ll y){
x=(x+y)%mod;
}
ll n,op[5005];
ll f[5005][5005],g[5005];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>op[i];
int b2=0,b3=0;
for(int i=1;i<=n;i++){
if(op[i]==2) b2=1;
if(op[i]==3) b3=1;
if(op[i]==0&&b2){
cout<<"0\n";
return 0;
}
if(op[i]==1&&b3){
cout<<"0\n";
return 0;
}
}
if(op[1]==0||op[1]==1) f[1][1]=1;
else f[1][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++){
upd(f[i][j],g[j]);
upd(g[j+1],g[j]);
}
if(i==n) break;
for(int j=0;j<=n;j++) g[j]=0;
for(int j=0;j<=i;j++){
if(op[i+1]==0||op[i+1]==1){
upd(f[i+1][j+1],f[i][j]);
}else{
upd(g[0],f[i][j]);
upd(g[j+1],mod-f[i][j]);
}
}
}
ll ans=0;
for(int i=0;i<=n;i++) upd(ans,f[n][i]);
cout<<ans<<"\n";
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...