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;}
}
posted @ 2020-03-30 17:37  chdy  阅读(120)  评论(0编辑  收藏  举报