UOJ22 外星人

题意

\(n\)个数,给出\(x\)
求出一个排列顺序,使\(n\)个数依次对\(x\)取模的最大值和方案数
$n\le 1000,x \le 5000 $
传送门

思路

终于不是一道神仙\(dp\)
考虑某个数,如果前面有数小于它,那么它存不存在都是没用的。
所以就可以从大到小考虑,分为两种:

  • 有用:那就必须紧挨着前一个\(dp[i][j\%a[i]]+=dp[i][j]\)
  • 无用:那么就要填到后面去,剩余\(n-i+1\)个,但不能在第一个,所以\(n-i\)
    \(dp[i][j]+=dp[i-1][j]*(n-i)\)

但是会出现全部选的情况,所以我就用\(0/1\)表示了前面有没有选过,好像也有不用讨论的方法
代码十分简短

#include <bits/stdc++.h>
#define upd(x,y) x=(x+y>=mu?x+y-mu:x+y)
const int N=1005,mu=998244353;
int n,x,a[N],dp[2][N][5005];
bool cmp(int x,int y){
	return x>y;
}
int main(){
	scanf("%d%d",&n,&x);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	std::sort(a+1,a+n+1,cmp);
	dp[0][0][x]=1;
	for (int i=1;i<=n;i++)
		for (int j=0;j<=x;j++){
			upd(dp[1][i][j%a[i]],dp[0][i-1][j]);//选 
			upd(dp[1][i][j%a[i]],dp[1][i-1][j]);
			upd(dp[1][i][j],dp[1][i-1][j]*1ll*(n-i)%mu);//不选 
			upd(dp[0][i][j],dp[0][i-1][j]*1ll*(n-i)%mu);
		} 
	for (int j=x;j>=0;j--){
		if (dp[1][n][j]){
			printf("%d\n%d\n",j,dp[1][n][j]);
			return 0;
		}
	}
} 

后记

难得的开心水题

posted @ 2019-11-04 16:17  flyfeather  阅读(124)  评论(0编辑  收藏  举报