P9135 [THUPC 2023 初赛] 快速 LCM 变换 题解
目录
题目描述
给定一个长为 的序列 ,对 ,删除 ,再加入 。
求得到的 个数列的最小公倍数之和,对 取模。
数据范围
- 。
时间限制 ,空间限制 。
分析
对 ,记 为原数组中含 的幂次最大、次大值。
那么原序列的 为 ,考虑以 为基准研究操作对 的影响。
:删除 ,再加入 , 含 幂次的增量为:
证明:不妨 。
- 如果 ,那么 ,因此后面的减法不会产生贡献。
- 如果 ,那么 ,增量为 。
- 如果 ,显然增量为零。
本题最巧妙的地方在于,上述结论仅对两个数的情形成立。
对于 个数,增量不能直接拆成关于 完全独立的变量。
令 ,则目标变为:
前者只需对值域做卷积,将 贡献到 的位置即可统计答案。
时间复杂度 。
#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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17365778.html
标签:
数论-多项式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?