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;
}
}
}
后记
难得的开心水题
* 生而自由 爱而无畏 *