1.8模拟赛 T2题解

发布时间 2024-01-08 21:06:33作者: hubingshan

简要题意

思路

先考虑啥样的 \(T\) 可能合法,就大概类似于一个一边删除,一边加入的操作,如果能删空,那就合法

但这样的 \(T\) ,不一定能作为答案,只有能将多余的数删除时才合法

那就用同样的策略,判断是否合法即可

接着考虑 \(T\) 的方案数咋求,设 \(dp_{i,j,k}\) ,表示从大到小填到数字 \(i\)\(0 \sim i-1\)共填了 \(j\) 次,后面可以多贡献 \(k\)\(0\),暴力转移,复杂度 \(O(n^2mlogm)\)

code

#include<bits/stdc++.h>
using namespace std;
#define N 105
int zu,n,ans,m,x;
int f[N][N][N],g[N][N],u[N];
const int mod=998244353;
void mo(int &x){
	if(x>=mod) x-=mod;
}
int chk(int num,int cnt){
	int xu=1,sum=0;
	for(int j=num-1;j>=1;j--){
		if(u[j]>=xu) cnt+=u[j]-xu;
		else xu+=xu-u[j];
		sum+=u[j];
		xu=min(xu,n+2);
	}
	if(!sum&&u[0]+cnt==0) return 1;
	if(u[0]+cnt+1>=xu) return 1;
	return 0;
}
int main(){
	freopen("normal.in","r",stdin);
	freopen("normal.out","w",stdout);
	scanf("%d",&zu);
	while(zu--){
		scanf("%d",&n);
		m=ans=0;
		memset(f,0,sizeof(f));memset(g,0,sizeof(g));memset(u,0,sizeof(u));
		for(int i=1;i<=n;i++) scanf("%d",&x),u[x]++;
		m=100;
		f[m+1][0][0]=1;
		for(int i=m;i>=1;i--){
			for(int j=0;j<=n;j++){
				for(int k=0;k<=n;k++){
					if(!f[i+1][j][k]) continue;
					for(int p=j;p<=n;p++){
						if(p<=u[i]){
							f[i][j][k+u[i]-p]+=f[i+1][j][k],mo(f[i][j][k+u[i]-p]);
							if(!j&&p>0) g[i][k+u[i]-p]+=f[i+1][j][k],mo(g[i][k+u[i]-p]);
						}
						else if(j+p-u[i]<=n) f[i][j+p-u[i]][k]+=f[i+1][j][k],mo(f[i][j+p-u[i]][k]);
					}
				}
			}
		}
		for(int j=0;j<=n;j++){//add:0
			for(int k=0;k<=n;k++){
				if(!f[1][j][k]) continue;
				for(int p=max(j,1);p<=n;p++){
					if(p<=u[0]+k) ans+=f[1][j][k],mo(ans);
				}
			}
		}
		for(int i=1;i<=m;i++){
			for(int k=0;k<=n;k++){
				if(!g[i][k]) continue;
				if(chk(i,k)) ans+=g[i][k],mo(ans); 
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}