题意
(n)个数,给出(x)
求出一个排列顺序,使(n)个数依次对(x)取模的最大值和方案数
$nle 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
%d
",j,dp[1][n][j]);
return 0;
}
}
}
后记
难得的开心水题