潜龙未见静水流,沉默深藏待时秋。一朝破空声势振,惊世骇俗展雄猷。
随笔 - 82, 文章 - 0, 评论 - 3, 阅读 - 2153

P9135 [THUPC 2023 初赛] 快速 LCM 变换 题解

目录

题目描述

给定一个长为 n 的序列 r ,对 1i<jn ,删除 ri,rj ,再加入 ri+rj

求得到的 n(n1)2 个数列的最小公倍数之和,对 998244353 取模。

数据范围

  • 2n5105,1ri106

时间限制 2s ,空间限制 512MB

分析

pprime ,记 a(p),b(p) 为原数组中含 p 的幂次最大、次大值。

那么原序列的 lcmM=pprimepa(p) ,考虑以 M 为基准研究操作对 lcm 的影响。

Key observation :删除 ri,rj ,再加入 ri+rjlcmp 幂次的增量为:

max(vp(ri+rj)a,0)[vp(ri)=a](ab)[vp(rj)=a](ab)

证明:不妨 vp(ri)vp(rj)

  • 如果 vp(ri+rj)a ,那么 vp(ri)=vp(rj) ,因此后面的减法不会产生贡献。
  • 如果 vp(ri)=a,vp(ri+rj)<a ,那么 vp(rj)<b ,增量为 ba
  • 如果 vp(ri)<a ,显然增量为零。

本题最巧妙的地方在于,上述结论仅对两个数的情形成立

对于 3 个数,增量不能直接拆成关于 r1,,rk,rk 完全独立的变量。

f(i)=pprime(1p)[vp(ri)=a](ab),g(i)=pprimepmax(vp(i)a,0) ,则目标变为:

1i<jnf(ri)f(rj)g(ri+rj)=12(i=1nj=1nf(ri)f(rj)g(ri+rj)i=1nf(ri)2g(2ri))

前者只需对值域做卷积,将 f(ri)f(rj) 贡献到 ri+rj 的位置即可统计答案。

时间复杂度 O(nV+VlogV)

#include<bits/stdc++.h>
using namespace std;
const int v=2e6,maxn=1<<21,mod=998244353,inv2=(mod+1)/2,inv3=(mod+1)/3;
int n,x,res;
int a[maxn],b[maxn],r[maxn];
int f[maxn],g[maxn],h[maxn],pw[25],cnt[maxn];
bool vis[maxn];
inline int qpow(int a,int k)
{
    int res=1;
    while(k)
    {
        if(k&1) res=1ll*res*a%mod;
        a=1ll*a*a%mod,k>>=1;
    }
    return res;
}
inline int add(int x,int y)
{
    if((x+=y)>=mod) x-=mod;
    return x;
}
inline int dec(int x,int y)
{
    if((x-=y)<0) x+=mod;
    return x;
}
inline void ntt(int *a,int n,int op)
{
    for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
    for(int k=2,m=1;k<=n;k<<=1,m<<=1)
    {
        int x=qpow(op==1?3:inv3,(mod-1)/k);
        for(int i=0;i<n;i+=k)
            for(int j=i,w=1;j<i+m;j++)
            {
                int v=1ll*a[j+m]*w%mod;
                a[j+m]=dec(a[j],v),a[j]=add(a[j],v);
                w=1ll*w*x%mod;
            }
    }
    if(op==-1)
    {
        int inv=qpow(n,mod-2);
        for(int i=0;i<n;i++) a[i]=1ll*a[i]*inv%mod;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&r[i]);
        int x=r[i];
        for(int j=2;j*j<=x;j++)
        {
            if(x%j) continue;
            int cnt=0;
            while(x%j==0) x/=j,cnt++;
            if(a[j]<=cnt) b[j]=a[j],a[j]=cnt;
            else b[j]=max(b[j],cnt);
        }
        if(x!=1)
        {
            if(a[x]<=1) b[x]=a[x],a[x]=1;
            else b[x]=max(b[x],1);
        }
    }
    for(int i=1;i<=v;i++) f[i]=g[i]=1;
    for(int p=2;p<=v;p++)
    {
        if(vis[p]) continue;
        int inv=qpow(p,mod-1-(a[p]-b[p]));
        for(long long i=0,x=1;x<=v;i++,x*=p) pw[i]=x;
        for(int i=p;i<=v;i+=p)
        {
            vis[i]=1,cnt[i]=cnt[i/p]+1;
            if(cnt[i]==a[p]) f[i]=1ll*f[i]*inv%mod;
            if(cnt[i]>a[p]) g[i]=1ll*g[i]*pw[cnt[i]-a[p]]%mod;
        }
        for(int i=p;i<=v;i+=p) cnt[i]=0;
    }
    for(int i=1;i<=n;i++)
    {
        int x=r[i];
        res=(res-1ll*f[x]*f[x]%mod*g[2*x])%mod;
        h[x]=add(h[x],f[x]);
    }
    n=1<<21;
    for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);
    ntt(h,n,1);
    for(int i=0;i<n;i++) h[i]=1ll*h[i]*h[i]%mod;
    ntt(h,n,-1);
    for(int i=0;i<n;i++) res=(res+1ll*h[i]*g[i])%mod;
    res=1ll*(res+mod)*inv2%mod;
    for(int p=2;p<=v;p++) if(a[p]) res=1ll*res*qpow(p,a[p])%mod;
    printf("%d\n",res);
    return 0;
}

posted on   peiwenjun  阅读(6)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示