#82. 【UR #7】水题生成器

链接:http://uoj.ac/problem/82

今天是世界水日,著名的水题资源专家蝈蝈大臣向世界宣布了他的一项新发明 —— 水题生成器。

每道题目都有一个正整数的难度值。水题生成器虽然强大但是功能有限。水题生成器内部有一个参数 nn,你可以告诉水题生成器一个能整除 n!n! 的正整数 dd,水题生成器就会产生一道难度值恰为 dd 的水题。这里 n!n! 表示 nn 的阶乘。

现在蝈蝈大臣的助手欧姆想用水题生成器产生不超过 nn 道水题,且难度值之和恰为 mm。保证 1≤m≤n!1≤m≤n!。

欧姆当然知道怎么做啦!但是他想考考你。请你给出一组合法方案或输出无解。

输入格式
第一行一个正整数 nn。

第二行一个正整数 mm。保证 1≤m≤n!1≤m≤n!。

输出格式
不超过 nn 行,每行一个正整数 dd,表示你每次告诉水题生成器的难度值。

输出的每个难度值都必须是 n!n! 的约数,且难度值之和恰为 mm。

如果有多组解,输出任意一组均可。如果无解请直接输出卖萌表情 “>w<”(不含引号)

样例一
input

5
100

output

40
40
20

explanation

5!=1×2×3×4×5=1205!=1×2×3×4×5=12020204040 都是 120120 的约数,且 40+40+20=10040+40+20=100。

样例二
input

10
3628800

output

3628800

限制与约定
测试点编号    nn的规模
1, 2, 3, 4, 5, 6    n≤5n≤5
7, 8, 9, 10, 11, 12, 13, 14    n≤9n≤9
15, 16, 17, 18, 19, 20    n≤20n≤20
时间限制:1s1s
空间限制:256MB
题干
其实我也不会看了题解。。。
还是直接看题接的解释吧:
        
水题生成器
from taorunz

算法一

对于前6个数据n≤5n≤55!=1205!=120,只有1616个约数。 我们直接用165165枚举这些子集,找到一个和等于mm的集合即可。

当然,由于nn很小,你还可以用分类讨论之类的方法乱搞。

期望得分:30分

算法二

对于前14个数据n≤9n≤9.

我们可以将本问题看成一个背包问题来解。

时间复杂度是O(d(n!)∗m)O(d(n!)∗m)的, 其中d(x)d(x)表示xx的约数。

期望得分:70分

算法三

本题与阶乘紧密相关,说到把不超过n!n!的数分解成nn个数的和,最容易想到的就是阶乘进位制。(就是分解成 ∑kak⋅k!∑kak⋅k! 且 ak≤kak≤k)

然而在本题里,用阶乘进位制分解得到的数不一定是n!n!的约数。 (例如样例一,阶乘进位制分解为100=96+4=4×4!+2×2!100=96+4=4×4!+2×2!,96却不是120约数)

怎么办呢? 把阶乘进位制倒过来!

我们令新的进位制的第kk位的位值为 n(n−1)⋯(n−k)n(n−1)⋯(n−k)
然后将mm分解,我们发现分解出来的数都是形如n(n−1)⋯(n−k)akn(n−1)⋯(n−k)ak的数。而akak是小于(n−k)(n−k)的!

这样n(n−1)⋯(n−k)akn(n−1)⋯(n−k)ak就一定是n!n!的约数,本题圆满解决啦!

期望得分:100分

算法四

不知道阶乘进位制?没关系!我们可以直接贪心!

我们先算出n!n!的所有约数(n=20n=20时有4104041040个),然后从大到小依次尝试减去当前数,直到减为零为止。

有趣的是,用这种方法,一定可以在不超过nn步内减到零!

为什么呢?我们参考算法三,对于当前剩余要造题的难度值之和m′m′ ,我们可以将它表示成反阶乘进位制后,取出最高位的数。这样就可以使得m′m′降低一个(反阶乘进位制的)数量级。而算法四所取出来的数不会比算法三取出的低,所以算法四也可以保证每次使得m′m′降低一个(反阶乘进位制的)数量级。而总共数量级最多为nn,一定能够在nn次内减到零。

期望得分:100分
题解
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL  unsigned long long 
LL  n,tot=1,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)    tot*=i;
    
    for(int i=1;i<=n;i++)
    {
        tot/=i;
        if(tot<=m)
        {
            printf("%lld ",tot*(m/tot));
            m-=tot*(m/tot);
        }
    }
    return 0;
} 
代码
原文地址:https://www.cnblogs.com/CLGYPYJ/p/7394662.html