zoukankan      html  css  js  c++  java
  • 【uoj#22】[UR #1]外星人 组合数学+dp

    题目描述

    给你一个长度为 $n$ 的序列 ${a_i}$ 和一个数 $x$ ,对于任意一个 $1sim n$ 的排列 ${p_i}$ ,从 $1$ 到 $n$ 依次执行 $x=x ext{mod} a_{p_i}$ ,最终得到一个数。求所有排列中能够得到的这个数的最大值,以及有多少种排列可以得到这个值。

    $nle 1000$ ,$xle 5000$ 。


    题解

    组合数学+dp

    由于 $a  ext{mod} b<b$ ,因此每次产生影响(即 $x ext{mod} a_i e x$)的 $a_i$ 一定是递减的。

    考虑将所有数从大到小排序处理。当确定了某个要产生影响的模数 $a_i$ ,$(x ext{mod} a_i,x]$ 中除了 $a_i$ 的数就都可以随便放在 $a_i$ 的后面,因为它们不会产生贡献。

    设 $f[i]$ 表示处理完 $(i,+infty)$ 的数,剩下的数为 $i$ 的方案数。

    那么考虑 $f[i]$ ,枚举下一个选择的数 $j$ ,令 $s[i]$ 表示小于等于 $i$ 的数的个数,则有 $f[i ext{mod} j]=f[i] imes A_{s[i]-1}^{s[i]-1-s[i ext{mod} j]}=f[i] imesfrac{(s[i]-1)!}{(s[i ext{mod} j])!}$ 。预处理阶乘及阶乘的逆元即可。

    时间复杂度 $O(n^2)$ 。

    #include <cstdio>
    #include <algorithm>
    #define N 1010
    #define M 5010
    #define mod 998244353
    using namespace std;
    typedef long long ll;
    int a[N] , sum[M];
    ll fac[N] , inv[N] , fin[N] , f[M];
    int main()
    {
    	int n , m = 5000 , x , i , j , mn = m;
    	scanf("%d%d" , &n , &x);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , sum[a[i]] ++ , mn = min(mn , a[i]);
    	for(i = 1 ; i <= m ; i ++ ) sum[i] += sum[i - 1];
    	fac[0] = fin[0] = fac[1] = inv[1] = fin[1] = 1;
    	for(i = 2 ; i <= n ; i ++ )
    	{
    		fac[i] = fac[i - 1] * i % mod;
    		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    		fin[i] = fin[i - 1] * inv[i] % mod;
    	}
    	f[x] = fac[n] * fin[sum[x]] % mod;
    	for(i = m ; i ; i -- )
    		for(j = 1 ; j <= n ; j ++ )
    			if(a[j] <= i)
    				f[i % a[j]] = (f[i % a[j]] + f[i] * fac[sum[i] - 1] % mod * fin[sum[i % a[j]]]) % mod;
    	for(i = mn - 1 ; ~i ; i -- )
    		if(f[i])
    			break;
    	printf("%d %lld" , i , f[i]);
    	return 0;
    }
    
  • 相关阅读:
    数组中只出现一次的数字
    Linux常用命令总结
    python之Django实现商城从0到1
    leetcode之转置矩阵
    leetcode之有序数组的平方
    数据结构与算法0—大纲
    TCP的三次握手与四次挥手
    常用SQL语句
    BeautifulSoup的基本用法
    关于测试
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8617946.html
Copyright © 2011-2022 走看看