CF1178F1题解

发布时间 2023-09-08 09:54:19作者: OIer_xxx2022

CF1178F1题解

根据题意,每次选择一个区间染色,我们可以发现这道题满足了区间 dp 的一些性质,即区间答案可以合并,大区间的答案可以由小区间的答案更新而来。那么我们就可以设 \(f_{i,j}\) 表示区间 \(i\)\(j\) 的答案,那么接下来就考虑如何转移。然后考虑到题目要求从 \(1\)\(n\) 进行染色,所以我们转移的时候先找到这段区间中颜色最小的一个地方,将其位置编号记为 \(pos\),那么 \(f_{i,j}\) 的答案则从 \(\left[i,k-1\right]\),
\(\left[k,pos-1\right]\),\(\left[pos+1,l\right]\),
\(\left[l+1,j\right]\) 这四个区间更新而来,转移的时候枚举 \(k,l\),时间复杂度 \(O(n^4)\),还不足以通过本题。

考虑优化这个算法,注意到 \(pos\) 两边对答案的贡献是互不影响的,所以转移时可以分开计算 \(pos\) 左右的答案,将两边的答案相乘即可实现转移,时间复杂度 \(O(n^3)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int inf=1e9;
const int N=510;
inline int read(){
	int f=1,w=0;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')	f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		w=(w<<1)+(w<<3)+(c^48);
		c=getchar();
	}
	return f*w;
}
int c[N];
int f[N][N];
int n,m; 
signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++){
		c[i]=read();
	}
	for(int i=1;i<=n+1;i++){
		f[i][i]=1;
		f[i][i-1]=1;//注意,在转移时可能出现第二维编号比第一维小1的情况 
	}
	for(int len=1;len<n;len++){
		for(int i=1;i+len<=n;i++){
			int j=i+len;
			int pos;
			int minn=inf;
			for(int k=i;k<=j;k++){
				if(c[k]<minn){
					minn=c[k];
					pos=k;
				}
			}
			int f1=0;
			for(int k=i;k<=pos;k++){
				f1=(f1+f[i][k-1]*f[k][pos-1])%mod;
			}
			int f2=0;
			for(int k=pos;k<=j;k++){
				f2=(f2+f[pos+1][k]*f[k+1][j])%mod;;    
			}
			f[i][j]=f1*f2%mod;
		}
	}
	cout<<f[1][n]<<endl;
	return 0;
}