DP+推式子+组合数+逆元
我们发现如果ai<=aj,那么x mod ai = x mod ai mod aj,即模了小的数,大的数模不模就都一样。于是我们发现x的值的变化仅和小于等于x的a有关。
于是可以记f[i]表示x=i的最大答案,这个可以O(nx)DP出来,第一问解决。
我们发现从i变化到i%a的过程中需要的序列里,(i%a , i]区间里的所有模数可以任意排布在a的后面而不会影响结果(因为这里面的数已经严格大于i%a),于是我们记g[i]表示i得到最大值且仅考虑小于等于i的模数的方案数,可以通过组合数学的知识推出转移式。最后再考虑上大于x的模数即可。
好像别人打的代码都比我的短啊……
#include<cstdio>
#include<algorithm>
#define N 1005
#define X 5005
#define MOD 998244353
using namespace std;
bool from[N][X];
const int INF = 1<<29;
long long inv[X], fac[X], inc[N], g[X];
int a[N], f[X], pre[X];
void init()
{
fac[0]=1;
fac[1]=1;
inv[1]=1;
inc[0]=1;
inc[1]=1;
for(int i = 2; i < N; i++)
{
fac[i]=fac[i-1]*i%MOD;
inv[i]=MOD-(MOD/i)*inv[MOD%i]%MOD;
inc[i]=inc[i-1]*inv[i]%MOD;
}
}
int main()
{
int n, x;
scanf("%d%d",&n,&x);
init();
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
for(int i = 1; i <= n; i++)
pre[a[i]]++;
for(int i = 0; i < X; i++)
pre[i]+=pre[i-1];
sort(a+1,a+1+n);
for(int i = 0; i < a[1]; i++)
{
f[i]=i;
g[i]=1;
}
for(int i = a[1]; i <= x; i++)
{
f[i]=-INF;
for(int j = 1; j <= n && a[j]<=i; j++)
if(f[i]<f[i%a[j]])f[i]=f[i%a[j]];
for(int j = 1; j <= n && a[j]<=i; j++)
if(f[i]==f[i%a[j]])
(g[i] += g[i%a[j]] * fac[pre[i]-1-pre[i%a[j]]] %MOD * fac[pre[i]-1] %MOD * inc[pre[i%a[j]]] %MOD * inc[pre[i]-1-pre[i%a[j]]] % MOD)%=MOD;
}
g[x] = fac[pre[X-1]-pre[x]] * g[x] %MOD * fac[pre[X-1]] %MOD * inc[pre[x]] %MOD * inc[pre[X-1]-pre[x]] % MOD ;
printf("%d\n%lld\n",f[x],(g[x]+MOD)%MOD);
}