题目描述

小 I 今天学习了快速最小公倍数变换(Fast Least-Common-Multiple Transform, FLT),于是他想考考你。

给定一个长度为 \(n\) 的正整数序列 \(r_1,r_2,\cdots,r_n\)。你需要做以下操作恰好一次:

  • 选择整数 \(i,j\) 使得 \(1 \le i < j \le n\)。在序列末尾加入 \((r_i+r_j)\),并将 \(r_i\) 和 \(r_j\) 从序列中删除。

可以注意到总共有 \(\frac{n(n-1)}{2}\) 种可能的操作,每种操作会得到一个长度为 \(n-1\) 的序列。

你需要对所有的这 \(\frac{n(n-1)}{2}\) 个序列,求出序列中所有元素的最小公倍数,并给出它们的和模 \(998244353\) 的值。

输入格式

输入的第一行包含一个正整数 \(n\),表示序列的长度。接下来一行 \(n\) 个正整数 \(r_1,r_2,\cdots,r_n\),描述初始序列。

输出格式

输出一行一个整数,表示所有序列的最小公倍数的和模 \(998244353\) 的值。

样例 #1

样例输入 #1

3
2 3 4

样例输出 #1

40

提示

样例解释 1

  • \(i=1,j=2\) 时,得到的序列为 \(\{4,5\}\),最小公倍数为 \(20\);
  • \(i=1,j=3\) 时,得到的序列为 \(\{3,6\}\),最小公倍数为 \(6\);
  • \(i=2,j=3\) 时,得到的序列为 \(\{2,7\}\),最小公倍数为 \(14\)。

因此输出为 \(20+6+14=40\)。

子任务

对于所有测试数据,\(2 \le n \le 5 \times 10^5, 1 \le r_1,r_2,\cdots,r_n \le 10^6\)。

题目来源

来自 2023 清华大学学生程序设计竞赛暨高校邀请赛(THUPC2023)初赛。

题解等资源可在 https://github.com/THUSAAC/THUPC2023-Pre 查看。

求最小公倍数还要取模?只能拆成质数之幂再乘起来了。

那么一个质数的哪些幂次最终可能会被计入答案呢?乍一看,次数前三大的都用到。但仔细分析一下,其实只用次数前两大的数。

这是因为,如果同时删掉了前两大次数的数,那么会增加 \(p^{k_1}+p^{k_2}\),那么他一定是 \(p^{k_2}\) 的倍数。

设原来的最小公倍数为 \(s\),那么如果删掉某一个数之后,答案会变成 \(s\) 的多少倍是固定的,也是分开的。我们可以枚举质数,算出删某一个数 \(x\) 时的答案 \(p_x\)。

此时如果我删掉数字 \(x\) 和 \(y\),那么此时 \(s\) 会变成 \(p_xp_ys\),然后我们还要统计 \(x+y\) 时带来的贡献。

可以通过 NTT 计算出选择和为 \(a\) 时的各种方案的 \(s\) 倍数之和,此时要计算 \(a\) 会变成 \(s\) 的多少倍。分解质因数后看他的次数是否超过 \(s\) 的次数就行了。

注意要好好去重。

#include<bits/stdc++.h>
using namespace std;
const int P=998244353,N=2097152,iv3=332748118,iv2=P+1>>1;
int a[N],f[N],p[N],cmx[N],mx[N],r[N],n,nw[N],ret=1,ans,c[N];
vector<int>g[N];
int pown(int x,int y)
{
if(!y)
return 1;
int t=pown(x,y>>1);
if(y&1)
return 1LL*t*t%P*x%P;
return 1LL*t*t%P;
}
void ntt(int a[],int op)
{
for(int i=0;i<N;i++)
if(i<r[i])
swap(a[i],a[r[i]]);
for(int md=1;md<N;md<<=1)
{
int g=pown(op? 3:iv3,(P-1)/(md<<1));
for(int i=0;i<N;i+=md<<1)
{
int pw=1;
for(int j=0;j<md;j++,pw=1LL*pw*g%P)
{
int ax=pw*1LL*a[i+j+md]%P;
a[i+j+md]=(a[i+j]-ax+P)%P;
(a[i+j]+=ax)%=P;
}
}
}
}
int main()
{
// freopen("P9135_1.in","r",stdin);
for(int i=2;i<N;i++)
if(!g[i].size())
for(int j=1;j*i<N;j++)
g[i*j].push_back(i);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i),f[a[i]]=1,c[a[i]]++;
for(int i=1;i<N;i++)
r[i]=r[i>>1]>>1|((i&1)*N/2);
for(int i=1;i<=n;i++)
{
for(int j=0;j<g[a[i]].size();j++)
{
int cnt=0,k=a[i];
while(k%g[a[i]][j]==0)
++cnt,k/=g[a[i]][j];
if(cnt>mx[g[a[i]][j]])
{
cmx[g[a[i]][j]]=mx[g[a[i]][j]],mx[g[a[i]][j]]=cnt;
nw[g[a[i]][j]]=a[i];
}
else if(cnt>cmx[g[a[i]][j]])
cmx[g[a[i]][j]]=cnt;
}
}
for(int i=1;i<N;i++)
if(mx[i])
f[nw[i]]=1LL*f[nw[i]]*pown(pown(i,mx[i]-cmx[i]),P-2)%P,ret=1LL*ret*pown(i,mx[i])%P;
for(int i=0;i<N;i++)
p[i]=f[i]*1ll*c[i]%P;
ntt(p,1);
for(int i=0;i<N;i++)
p[i]=1LL*p[i]*p[i]%P;
ntt(p,0);
for(int i=0;i<N;i++)
p[i]=1LL*p[i]*pown(N,P-2)%P;
// for(int i=1;i<=8;i++)
// printf("%d \n",p[i]);
for(int i=1;i<N;i++)
{
if(i*2<N)
(p[i*2]+=P-1LL*c[i]*f[i]%P*f[i]%P)%=P;
// if(i<=8)
// printf("%d ",p[i]);
for(int j=0;j<g[i].size();j++)
{
int cnt=0,k=i;
while(k%g[i][j]==0)
++cnt,k/=g[i][j];
if(cnt>mx[g[i][j]])
p[i]=1LL*p[i]*pown(g[i][j],cnt-mx[g[i][j]])%P;
}
(ans+=p[i])%=P;
}
printf("%lld\n",ans*1LL*ret%P*iv2%P);
}

[THUPC 2023 初赛] 快速 LCM 变换的更多相关文章

  1. 为什么要进行傅立叶变换?傅立叶变换究竟有何意义?如何用Matlab实现快速傅立叶变换

    写在最前面:本文是我阅读了多篇相关文章后对它们进行分析重组整合而得,绝大部分内容非我所原创.在此向多位原创作者致敬!!!一.傅立叶变换的由来关于傅立叶变换,无论是书本还是在网上可以很容易找到关于傅立叶 ...

  2. 离散傅立叶变换与快速傅立叶变换(DFT与FFT)

    自从去年下半年接触三维重构以来,听得最多的词就是傅立叶变换,后来了解到这个变换在图像处理里面也是重点中的重点. 本身自己基于高数知识的理解是傅立叶变换是将一个函数变为一堆正余弦函数的和的变换.而图像处 ...

  3. 快速傅里叶变换 & 快速数论变换

    快速傅里叶变换 & 快速数论变换 [update 3.29.2017] 前言 2月10日初学,记得那时好像是正月十五放假那一天 当时写了手写版的笔记 过去近50天差不多忘光了,于是复习一下,具 ...

  4. 快速傅立叶变换(FFT)算法

    已知多项式f(x)=a0+a1x+a2x2+...+am-1xm-1, g(x)=b0+b1x+b2x2+...+bn-1xn-1.利用卷积的蛮力算法,得到h(x)=f(x)g(x),这一过程的时间复 ...

  5. $\mathcal{FFT}$·$\mathcal{Fast \ \ Fourier \ \ Transformation}$快速傅立叶变换

    \(2019.2.18upd:\) \(LINK\) 之前写的比较适合未接触FFT的人阅读--但是有几个地方出了错,大家可以找一下233 啊-本来觉得这是个比较良心的算法没想到这么抽搐这个算法真是将一 ...

  6. BZOJ 2194 快速傅立叶变换之二 | FFT

    BZOJ 2194 快速傅立叶变换之二 题意 给出两个长为\(n\)的数组\(a\)和\(b\),\(c_k = \sum_{i = k}^{n - 1} a[i] * b[i - k]\). 题解 ...

  7. 快速莫比乌斯变换(FMT)

    快速莫比乌斯变换(FMT) 原文出处:虞大的博客.此仅作蒟蒻本人复习用~ 给定两个长度为n的序列 \(a_0, a_1, \cdots, a_{n-1}\)和\(b_0, b_1, \cdots, b ...

  8. 快速傅立叶变换(FFT)

    多项式 系数表示法 设\(f(x)\)为一个\(n-1\)次多项式,则 \(f(x)=\sum\limits_{i=0}^{n-1}a_i*x_i\) 其中\(a_i\)为\(f(x)\)的系数,用这 ...

  9. 集合并卷积的三种求法(分治乘法,快速莫比乌斯变换(FMT),快速沃尔什变换(FWT))

    也许更好的阅读体验 本文主要内容是对武汉市第二中学吕凯风同学的论文<集合幂级数的性质与应用及其快速算法>的理解 定义 集合幂级数 为了更方便的研究集合的卷积,引入集合幂级数的概念 集合幂级 ...

  10. 【算法】快速数论变换(NTT)初探

    [简介] 快速傅里叶变换(FFT)运用了单位复根的性质减少了运算,但是每个复数系数的实部和虚部是一个余弦和正弦函数,因此系数都是浮点数,而浮点数的运算速度较慢且可能产生误差等精度问题,因此提出了以数论 ...

随机推荐

  1. 【全新重构发布】iNeuOS工业互联网操作系统 V5 版本

    iNeuOS工业互联网操作系统历经迭代27个版本,老版本截止更新V4.1版本:历时一年多时间,现在正式发布全新V5版本,V5版本与V4.1老版本不完全兼容. 发布V5版本后,iNeuOS工业互联网操作 ...

  2. SQL技巧:WITH RECURSIVE递归运算

    SQL技巧:WITH RECURSIVE递归运算 1.累加求和运算 -- 计算递归 WITH RECURSIVE t(n) AS (     VALUES (1)   UNION ALL     SE ...

  3. TDD、BDD、ATDD都是什么、有什么区别?(上)

    软件开发是一个迭代过程,包括编写.测试和改进代码,直到满足需求.测试驱动开发(TDD).行为驱动开发(BDD)和验收测试驱动开发(ATDD)是支持该过程的三种方法.TDD.BDD和ATDD都是软件开发 ...

  4. 青语言V1.0正式发布

    大家好,距离6月1日青语言发布第一个版本已经过去了三个月,而今我们按计划发布青语言的1.0版本. 青语言主页:https://qingyuyan.cn V1发布宣传视频:https://www.bil ...

  5. MQ系列14:MQ如何做到消息延时处理

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...

  6. Vika and Her Friends

    Smiling & Weeping ----早知道思念那么浓烈,不分手就好了 题目链接:Problem - A - Codeforces 题目大意:有n个Vika的朋友在一个n*m的方格中去捉 ...

  7. 分享一个 SpringBoot + Redis 实现「查找附近的人」的小技巧

    前言 SpringDataRedis提供了十分简单的地理位置定位的功能,今天我就用一小段代码告诉大家如何实现. 正文 1.引入依赖 <dependency> <groupId> ...

  8. KRpano项目微信出现"关于潜在的违法或违规内容"

    最近,部分小伙伴反应某些KRPano项目在微信中,出现"关于潜在的未发或违规内容"而无法播放的问题,会看到下图中的提示: 出现原因 这个问题是由于KRPano项目中的webvr.j ...

  9. Solution -「CF 1073G」Yet Another LCP Problem

    Description Link. 给定字符串,正整数集合 \(A,B\),满足 \(\forall u\in A,v\in B,1\le u,v\le n\). 求 \(\sum_{i\in A}\ ...

  10. destoon根据标题删除重复数据

    因为采集数据比较庞大,难免出现重复数据,所以写了一个根据标题进行删除重复数据的mysql命令,需要的朋友可以使用. 1 2 3 4 DELETE from destoon_article_36 whe ...