1 条题解

  • 0
    @ 2025-8-24 22:08:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar LZDQ
    **

    搬运于2025-08-24 22:08:20,当前版本为作者最后更新于2019-02-07 12:05:46,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    看完求点赞,我每看一篇不管好不好都点赞的

    我看没人写,自己随便写一点吧

    这题其实很像小学奥数

    简化题目:对于每个花田,只要求有多少种情况这个花田会被采到。

    也就是说,这个花田前面不能有它的因子,求排列总数。


    首先我们考虑的是,一个花田有多少个因子。

    排列时,这个花田必须排在所有它的因子前面,剩下不是它的因子的数,随便排

    小学奥数闪亮登场

    先看看一组自己的数据

    6
    1 1 2 2 2 3
    

    我们只考虑3的排列

    根据小学奥数,先把3和两个1排好

    3 1 1
    

    当然,两个1的位置可以随意排列,先把方案数乘 2!2!

    接着把剩下的三个数,三个2随意在这些空位放

    第一个2,可以放在以下位置:

    _ 3 _ 1 _ 1 _
    

    方案数乘4

    第二个2有5个位置可以放,方案数乘5

    以此类推

    注意最后会乘到 n ,我比赛的时候没发现结果还绕弯路打 st 表,不过还是过了

    有人会问,要不要乘 3!3!

    不用,因为已经是随便排的了


    细节问题

    求一个花田在其他花田中有多少个因子

    这里不能用 O(n2)O(n^2) 的暴力,必须优化

    我们可以用桶排,从小到大枚举,枚举到一个数就把它的倍数用另一个桶表示

    复杂度 $O(10^5*(1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{10^5}))$

    用 cnta 数组表示花田为 i 的数量(这句话看不懂可以看代码)

    for(int i=1;i<=n;i++){
    	int a;  //花的数量
    	scanf("%d",&a);  //读入
        cnta[a]++;  //桶排
    }
    

    接着,用 cntb 数组表示这个数有多少个因子

    for(int i=1;i<=1e5;i++)  //枚举桶
    	if(cnta[i]){      //如果有这个数
    		for(int j=i;j<=1e5;j+=i)  //枚举倍数
    			cntb[j]+=cnta[i];  //这段代码看这里
    	}
    

    然后,if 语句后面有大括号,就是求解过程

    注意 cntb 那里 i 也是算了的,因为可能有多个相同的数,比如样例中有两个3。这是为了数组表意更明确,因子也包括自己

    已知一个数有 x 个因子,方案数为:

    (x1)!n!x!(x-1)! * \frac{n!}{x!}

    变成 n!x\frac{n!}{x} 这里要注意,有取模,所以我们不能把 n!n! 存下来再除以 x

    其实刚才那个已经可以 O(1)O(1) 求解了

    用一个数组表示 i!i!,另一个数组表示 n!i!\frac{n!}{i!}

    这两个东西都是 O(n)O(n) 预处理的

    于是这题就到此为止了

    代码

    #include<cstdio>
    const int MAXN=1e5+5,MOD=998244353;
    int n;
    int f1[MAXN],f2[MAXN];
    int cnta[MAXN],cntb[MAXN];
    int ans;
    int main(){
        scanf("%d",&n);
        f1[0]=1;
        for(int i=1;i<=n;i++)
            f1[i]=1ll*f1[i-1]*i%MOD;
        f2[n+1]=1;
        for(int i=n;i>0;i--)
            f2[i]=1ll*f2[i+1]*i%MOD;
        for(int i=1;i<=n;i++){
            int a;
            scanf("%d",&a);
            cnta[a]++;
        }
        for(int i=1;i<=1e5;i++)
            if(cnta[i]){
                for(int j=i;j<=1e5;j+=i)
                    cntb[j]+=cnta[i];
                ans=(ans+1ll*i*cnta[i]%MOD*f1[cntb[i]-1]%MOD*f2[cntb[i]+1]%MOD)%MOD;
            }
        printf("%d\n",ans);
        return 0;
    }
    

    话说这是“超水的签到题”吗,还有紫题啊,我这个初一的蒟蒻还能做出来,不可思议!

    看完博客记得点赞啊,我每看一篇都点的

    • 1

    信息

    ID
    4176
    时间
    1000ms
    内存
    125MiB
    难度
    4
    标签
    (无)
    递交数
    0
    已通过
    0
    上传者