好题集(8) - CF 757E Bash Plays with Functions

题目传送门

题意

定义数论函数 \(f_r\) 如下:

\[f_r(n)= \begin{cases} \sum\limits_{d\mid n}[d\perp \frac{n}{d}]&,r=0\\ \sum\limits_{d\mid n}\frac{f_{r-1}(d)+f_{r-1}(\frac{n}{d})}{2}&,r>0 \end{cases} \]

现给定 \(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)\)

将定义式简单变一下形:

\[\begin{align*} f_r(n) &=\sum\limits_{d\mid n}\frac{f_{r-1}(d)+f_{r-1}(\frac{n}{d})}{2}\\ &=\sum\limits_{d\mid n}f_{r-1}(d) \end{align*} \]

其中最后一步的原理是 \((u,v)\) 是有序对,每个 \(d\) 都会出现两次。得证。

引理 \(4\)\(\forall r\in\mathbb{N},f_r\) 是积性函数

考虑使用归纳法,分成两个子引理来证明。

引理 \(4.1\)\(f_0\) 是积性函数

\(a,b\) 为两互质的正整数,则由引理 \(1\)、引理 \(2\) 知:

\[\begin{align*} f_0(a)\cdot f_0(b)&=2^{\omega(a)}\cdot 2^{\omega(b)}\\ &=2^{\omega(a)+\omega(b)}\\ &=2^{\omega(ab)}\\ &=f_0(ab) \end{align*} \]

引理 \(4.1\) 得证。

引理 \(4.2\)\(\forall r\in\mathbb{N_{+}}\),若 \(f_{r-1}\) 是积性函数,则 \(f_r\) 也是积性函数

\(x,y\) 为两互质的正整数。

\[\begin{align*} f_r(x\cdot y)&=\sum\limits_{d\mid(x\cdot y)}f_{r-1}(d)\\ &=\sum\limits_{d_1\mid x,d_2\mid y}f_{r-1}(d_1\cdot d_2)\\ &=\sum\limits_{d_1\mid x,d_2\mid y}f_{r-1}(d_1)\cdot f_{r-1}(d_2)\\ &=\Big(\sum\limits_{d\mid x}f_{r-1}(d)\Big)\cdot\Big(\sum\limits_{d\mid y}f_{r-1}(d)\Big)\\ &=f_r(x)\cdot f_r(y) \end{align*} \]

引理 \(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)\)

再简单推一下:

\[\begin{align*} f_r(p^k)&=\sum\limits_{d\mid p^k}f_{r-1}(d)\\ &=\sum\limits_{i=0}^k f_{r-1}(p^i) \end{align*} \]

因此由引理 \(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;
}

提交记录

posted @ 2026-01-09 17:03  DX3906_ourstar  阅读(2)  评论(0)    收藏  举报