好题集(8) - CF 757E Bash Plays with Functions
题意
定义数论函数 \(f_r\) 如下:
现给定 \(q\) 次询问,每次询问给定 \(r,n\),求 \(f_r(n)\bmod(10^9+7)\) 的值。\(1\le r,n,q\le 10^6\)。
一堆引理
首先来证明若干结论,以便随后解题。
记 \(\omega(n)\) 表示 \(n\) 的不同质因子的个数。
引理 \(1\):若 \(a\perp b\),则 \(\omega(ab)=\omega(a)+\omega(b)\)
设 \(a,b\) 为两互质的正整数,质因数分解得到 \(a=\prod\limits_{i=1}^{\omega(a)}p_i^{\alpha_i},b=\prod\limits_{i=1}^{\omega(b)}q_i^{\beta_i}\),其中 \(\forall p_i,q_i\in\mathbb{P}\)。
将两式相乘,得到 \(ab=(\prod\limits_{i=1}^{\omega(a)}p_i^{\alpha_i})\cdot(\prod\limits_{i=1}^{\omega(b)}q_i^{\beta_i})\)。
由 \(a\perp b\) 知 \(p_i,q_i\) 两两不同,因此上式中没有可以合并的幂。也就是说,\(ab\) 由 \(\omega(a)+\omega(b)\) 个不同的质因子相乘得到,即 \(\omega(ab)=\omega(a)+\omega(b)\),得证。
引理 \(2\):\(f_0(n)=2^{\omega(n)}\)
发现 \(d\perp \frac{n}{d}\) 就意味着 \(d\) 和 \(\frac{n}{d}\) 没有公共质因子。那么 \(n\) 的每个质因子 \(p_i\) 就有两种选择:作为 \(d\) 的质因子或是作为 \(\frac{n}{d}\) 的质因子。一共有 \(\omega(n)\) 个不同质因子,那么 \(f_0(n)=2^{\omega(n)}\),得证。
引理 \(3\):\(f_r(n)=\sum\limits_{d\mid n}f_{r-1}(d)\)
将定义式简单变一下形:
其中最后一步的原理是 \((u,v)\) 是有序对,每个 \(d\) 都会出现两次。得证。
引理 \(4\):\(\forall r\in\mathbb{N},f_r\) 是积性函数
考虑使用归纳法,分成两个子引理来证明。
引理 \(4.1\):\(f_0\) 是积性函数
设 \(a,b\) 为两互质的正整数,则由引理 \(1\)、引理 \(2\) 知:
引理 \(4.1\) 得证。
引理 \(4.2\):\(\forall r\in\mathbb{N_{+}}\),若 \(f_{r-1}\) 是积性函数,则 \(f_r\) 也是积性函数
设 \(x,y\) 为两互质的正整数。
引理 \(4.2\) 得证。
结合上述两个引理,引理 \(4\) 得证。
引理 \(5\):\(\forall p\in\mathbb{P},k\in\mathbb{N},f_0(p^k)=2\)
显然只有 \(d=1\) 和 \(d=p^k\) 时,才有 \(d\mid p^k\) 且 \(\frac{p^k}{d}\perp d\)。因此函数值为 \(2\)。
解法
对 \(n\) 质因数分解,拆成 \(\prod\limits_{i=1}^{\omega(n)}p_i^{\alpha(i)}\) 的形式,其中 \(\forall p_i\in\mathbb{P}\)。这一步可以线性筛 \(O(n)\) 实现。于是由引理 \(4\),我们得到 \(f_r(n)=\sum\limits_{i=1}^{\omega(n)}f_r(p^{\alpha(i)})\)。
于是考虑 \(\forall p\in\mathbb{P},k\in\mathbb{N},r\in\mathbb{N}\),如何求 \(f_r(p^k)\)。
再简单推一下:
因此由引理 \(5\),\(f_r(p^k)\) 的值实际上与 \(p\) 无关。
于是基于这个式子,我们可以 \(O(r\cdot k)\) 预处理,\(O(\log n)\) 查询。\(p=2\) 时 \(k\) 可以取到最大值 \(\log n\),而 \(n,r\) 同阶,因此预处理复杂度是 \(O(n\log n)\)。
这里再补充一个问题:如何用线性筛做到 \(O(n)\) 预处理,\(O(\log n)\) 求 \([1,n]\) 中某个整数 \(x\) 的质因数分解?
可以想到在筛法过程中记录每个数 \(v\) 的最小质因子,记为 \(e_v\)。求 \(x\) 的质因数分解时,可以用一重循环不断地将 \(x\) 除以 \(e_x\),直到得到的 \(x^{\prime}\) 不是 \(e_x\) 的倍数。此时循环的轮数即为 \(x\) 的质因数分解中 \(e_x\) 的次数。对最后得到的 \(x^{\prime}\) 重复这一过程,直到 \(x^{\prime}=1\) 即可。
代码:
#include<iostream>
#define ll long long
using namespace std;
const int N=1e6+5;
int q;
namespace OIfast{
char buf[1<<21],*p1,*p2,buffer[1<<21];
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?0:*p1++)
#define gc getchar()
inline int read(){
int n=0;static char c=gc;
while(!isdigit(c))c=gc;
while(isdigit(c))n=(n<<3)+(n<<1)+(c^48),c=gc;
return n;
}
}using namespace OIfast;
namespace Math{
const int p=1e9+7;
inline ll mul(ll a,ll b){
return (((a%p)+p)*((b%p)+p))%p;
}
inline void mult(ll &a,ll b){
return a=mul(a,b),void();
}
int tot;
ll pri[N],e[N];
ll f[N][25];
/*
f[r][k] -> f_r(p^k),p\in\mathbb{P}
*/
inline void init(int n){
for(int i=2;i<=n;++i){
if(!e[i])e[i]=pri[++tot]=i;
for(int j=1;i*pri[j]<=n&&j<=tot;++j){
if(e[i*pri[j]]==pri[j])break ;//写成标准的 if(!(i%pri[j]))break ; 亦可
e[i*pri[j]]=pri[j];
}
}
for(int r=1;r<=n;++r)f[r][0]=1;
for(int k=1;k<=24;++k)f[0][k]=2;
for(int r=1;r<=n;++r)for(int k=1;k<=24;++k)f[r][k]=(f[r][k-1]+f[r-1][k])%p;
return ;
}
inline ll calc(int r,int n){
ll res=1;
while(n>1){
int cnt=0,v=e[n];
while(!(n%v))n/=v,++cnt;
mult(res,f[r][cnt]);
}
return res;
}
}using namespace Math;
signed main(){
init(1e6+1);
q=read();
while(q--){
int r=read(),n=read();
printf("%lld\n",calc(r,n));
}
return 0;
}
提交记录。

浙公网安备 33010602011771号