UOJ #22 UR #1 外星人
LINK:#22. UR #1 外星人
给出n个正整数数 一个初值x x要逐个对这些数字取模 问怎样排列使得最终结果最大 使结果最大的方案数又多少种?
n<=1000,x<=5000.
考虑一个排列真正的有效取模只有当 \(x\geq a_i\)时才行 所以x通过一个排列真正有效的数字必然是从大到小排列的。
求第一问 不难想到将模数从大到小排列 设f[i][j]表示到达第i个模数此时值为j是否可行。
这样dp下来我们只需要取出小于minn的那个可行值最大的即可。
考虑方案数 这样dp同样有效。
不过复杂度 \(n^2\cdot x\) 比较高。
考虑优化 不难发现对于f[i][j]来说 如果 j比\(a_{i-1}\)还要小 那么没有必要再把状态传到i-1了 直接让i-1在后面位置中随便选择一个位置即可。
设状态f[i]表示权值到达i的方案数.
此时只有<=i的模数才能优影响 且前面的所有模数已经被我们安排好位置了。
如果选择了一个模数 \(a_j\) 那么 设w=i%\(a_j\) 权值在i-1到w+1之间的模数便没用了随便排个位置即可。
复杂度\(nx\) 可以通过.(值得一提的是 最后能获得的最大值尽量用标记来判断 因为方案数要取模,(刚好模为0 就脸黑了.
const int MAXN=5010;
int n,m,minn;
int a[MAXN],vis[MAXN],b[MAXN];
ll f[MAXN],inv[MAXN],fac[MAXN];
inline ll ksm(ll b,int p){ll cnt=1;while(p){if(p&1)cnt=cnt*b%mod;b=b*b%mod;p=p>>1;}return cnt;}
inline void prepare()
{
fac[0]=1;
rep(1,n,i)fac[i]=fac[i-1]*i%mod;
inv[n]=ksm(fac[n],mod-2);
fep(n-1,0,i)inv[i]=inv[i+1]*(i+1)%mod;
}
int main()
{
freopen("1.in","r",stdin);
get(n);get(m);minn=INF;
rep(1,n,i)get(a[i]),minn=min(minn,a[i]),++vis[a[i]];
rep(1,m,i)vis[i]+=vis[i-1];
prepare();f[m]=fac[n]*inv[vis[m]]%mod;
sort(a+1,a+1+n);
fep(m,1,i)
{
if(!f[i])continue;
rep(1,n,j)
{
if(a[j]>i)break;
f[i%a[j]]=(f[i%a[j]]+f[i]*fac[vis[i]-1]%mod*inv[vis[i%a[j]]])%mod;
}
}
fep(minn-1,0,i)if(f[i]){printf("%d %lld\n",i,f[i]);return 0;}
}