题解:[ARC112E] Cigar Box

前言

好题,很可做也很有意思。

思路分析

首先发现如果一个点被操作了多次,那么只有最后一次会影响它的位置。

我们称影响位置的操作为有效操作,不影响位置的为无效操作。

不难发现,序列可以分为三部分,前面部分是往前放的有效操作,中间部分是没有移动过的,后面部分是往后放的有效操作。

于是可以考虑一个 DP,设 \(f_{i,l,r}\) 表示从后往前考虑操作,目前填了 \(i\) 个,有 \(l\) 个往前放的有效操作,有 \(r\) 个往后放的有效操作。转移为:

\[f_{i,l,r}=2(l+r)f_{i-1,l,r}+f_{i-1,l-1,r}+f_{i-1,l,r-1} \]

前一部分表示第 \(i\) 次操作是无效操作,那么它可以放到序列中任意位置,任意方向;

后两部分表示第 \(i\) 次操作是有效操作,因为变换后的序列是唯一的,所以这次操作也是唯一的。

统计答案时,需要考虑中间的部分是否合法,也就是 \(a[l+1,n-r]\) 是否单调递增。

考虑进一步优化。

发现 \(f_{i,l,r}\) 的转移之和 \((l+r)\) 有关,也就是说 \(l+r\) 相等的 \(f_{i,l,r}\) 的值是一样的。

这启发我们改进 DP,设 \(g_{i,j}\) 表示从后往前考虑操作,目前填了 \(i\) 个,有 \(j\) 个有效操作。转移为:

\[g_{i,j}=g_{i-1,j-1}+2jg_{i-1,j} \]

在统计答案时,因为没有钦定 \(j\) 次有效操作的顺序,所以 \(f_{i,l,r}=\binom{l+r}{l}g_{i,l+r}\)

复杂度 \(O(nm)\)

代码实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,m,ans,a[3005],f[3005][3005],c[3005][3005],vis[3005][3005];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	f[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=n;j++){
			f[i][j]=((j?f[i-1][j-1]:0)+2*j*f[i-1][j]%mod)%mod;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			if(i!=j && a[j-1]>a[j]) break;
			vis[i][j]=1;
		}
	}
	c[0][0]=1;
	for(int i=1;i<=m;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;
		}
	}
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++){
			if(i+1>n-j || vis[i+1][n-j]) ans=(ans+f[m][i+j]*c[i+j][i]%mod)%mod;
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2025-03-28 17:23  _Kenma  阅读(21)  评论(0)    收藏  举报