CF757E Bash Plays with Functions
Statement
- f 0 ( n ) = ∑ p ⋅ q = n [ gcd ( p , q ) = 1 ] f_0(n)=\sum\limits_{p\cdot q=n}[\gcd(p,q)=1] f0(n)=p⋅q=n∑[gcd(p,q)=1]
- f r + 1 ( n ) = ∑ u ⋅ v = n f r ( u ) + f r ( v ) 2 f_{r+1}(n)=\sum\limits_{u\cdot v=n}\frac{f_r(u)+f_r(v)}{2} fr+1(n)=u⋅v=n∑2fr(u)+fr(v)
q ( 1 ≤ q ≤ 1 0 6 ) q(1\leq q\leq10^6) q(1≤q≤106)次询问, 每次询问输出 f r ( n ) mod 1 0 9 + 7 ( 0 ≤ r ≤ 1 0 6 , 1 ≤ n ≤ 1 0 6 ) f_r(n)\text{ mod }10^9+7(0\leq r\leq10^6,1\leq n\leq10^6) fr(n) mod 109+7(0≤r≤106,1≤n≤106).
Solution
设 n = ∏ i = 1 k p i α k n=\prod_{i=1}^kp_i^{\alpha_k} n=∏i=1kpiαk、 u = ∏ i = 1 k p i β i u=\prod_{i=1}^kp_i^{\beta_i} u=∏i=1kpiβi、 v = ∏ i = 1 k p i α i − β i v=\prod_{i=1}^kp_i^{\alpha_i-\beta_i} v=∏i=1kpiαi−βi, 满足 n = u × v , ∀ k ∈ [ 1 , k ] , p i n=u\times v,\forall k\in[1,k],p_i n=u×v,∀k∈[1,k],pi是质数.
- 性质1:则显然有 gcd ( u , v ) = 1 \gcd(u,v)=1 gcd(u,v)=1的充要条件是 ∀ i ∈ [ 1 , k ] \forall i\in[1,k] ∀i∈[1,k]满足 β i = 0 或 α i − β i = 0 \beta i=0或\alpha_i-\beta_i=0 βi=0或αi−βi=0.
- 性质2: 由性质1可得 f 0 ( ∏ i = 1 k p i α k ) = 2 k f_0(\prod_{i=1}^kp_i^{\alpha_k})=2^k f0(∏i=1kpiαk)=2k, 即要么 β i = 0 \beta_i=0 βi=0要么 α i − β i = 0 \alpha_i-\beta_i=0 αi−βi=0共 2 k 2^k 2k种不同的方案数.
- 性质3:
f
r
+
1
(
n
)
=
∑
u
⋅
v
=
n
f
r
(
u
)
+
f
r
(
v
)
2
=
∑
d
∣
n
f
r
(
d
)
f_{r+1}(n)=\sum\limits_{u\cdot v=n}\frac{f_r(u)+f_r(v)}{2}=\sum\limits_{d|n}f_r(d)
fr+1(n)=u⋅v=n∑2fr(u)+fr(v)=d∣n∑fr(d).
- 证明: 显然 u ≤ v u\leq v u≤v是成对出现的. 对于每一个因子 u u u都会被计算两次.
- 性质4: f r ( n ) f_{r}(n) fr(n)对任意 r ≥ 0 r\geq0 r≥0都满足 f r ( n ) f_r(n) fr(n)是积性函数.
- 性质5: f r ( p α ) = ∑ i = 0 α f r − 1 ( p i ) f_r(p^\alpha)=\sum_{i=0}^\alpha f_{r-1}(p^i) fr(pα)=∑i=0αfr−1(pi).
显然 f r ( p α ) f_r(p^\alpha) fr(pα)与 p p p是具体那个素数是无关的. 我们不妨记录 g i , j = f i ( p j ) g_{i,j}=f_i(p^j) gi,j=fi(pj)的答案.有 g 0 , 0 = 1 , g 0 , i = 2 ( i > 1 ) g_{0,0}=1,g_{0,i}=2(i>1) g0,0=1,g0,i=2(i>1). g i , j = ∑ k = 0 j g i − 1 , j g_{i,j}=\sum_{k=0}^jg_{i-1,j} gi,j=∑k=0jgi−1,j. 我们预处理出 g i , j g_{i,j} gi,j时间复杂度为 O ( 20 n ) O(20n) O(20n).
然后我们用线性筛法计算出, 每个数的质因子分解即可. 时间复杂度 O ( n ) O(n) O(n). 此处可以利用欧拉筛法, 因为欧拉筛法每个素数只会被最小质数筛到, 每个数被筛到时, 我们记录一下这是不是新的质因子, 如果是往结尾加 1 1 1, 否则将最后一个数出现次数 + 1 +1 +1即可.
Code
# define Fast_IO std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
# include "unordered_map"
# include "algorithm"
# include "iostream"
# include "cstdlib"
# include "cstring"
# include "cstdio"
# include "vector"
# include "bitset"
# include "queue"
# include "cmath"
# include "ctime"
# include "map"
# include "set"
# define ll long long
# define ld long double
# define rep1(i,a,b) for(ll i=(a);i<=(b);i++)
# define rep2(i,a,b) for(ll i=(a);i>=(b);i--)
# define pii pair<int,int>
# define pll pair<ll,ll>
# define pb push_back
# define eb emplace_back
# define vi vector<int>
# define vll vector<ll>
# define vpi vector<pii >
# define vpll vector<pll >
# define ri(x) scanf("%d",&x)
# define rf(x) scanf("%f",&x)
# define rl(x) scanf("%lld",&x)
# define rd(x) scanf("%lf",&x)
# define rs(s) scanf("%s",s+1)
# define wi(x) printf("%d",x)
# define wl(x) printf("%lld",x)
# define ws(s) printf("%s",s+1)
# define resize(v,x) v.resize(x)
# define all(v) v.begin(),v.end()
# define reverse(v) reverse(all(v))
# define fi first
# define se second
# define lowbit(x) ((x)&(-(x)))
# define repauto(Name,v) for(auto Name:v)
using namespace std;
template<class I> inline I GCD(I A,I B){return B?GCD(B,A%B):A;}
template<class I> inline I LCM(I A,I B){return A/GCD(A,B)*B;}
template<class I> I Sqrt(I N){
I sqrtN=sqrt(N)-1;
while(sqrtN+1<=N/(sqrtN+1))sqrtN++;
return sqrtN;
}
template<class I> I Pow(I X,I Y,__int128 Mod1=998244353){
static __int128 Ans; Ans=1;
for(;Y;Y>>=1,X=(__int128)X*X%Mod1) if(Y&1) Ans=Ans*X%Mod1;
return Ans;
}
const int Mod=1e9+7;
const int maxm=1e6+10;
int T,R,N;
int F[maxm][21];
long long Ans,Sum;
namespace Count_Divisor_Number_Class{
int Prime_Cnt;
int *Prime=new int;
int *Visit=new int;
int *Num=new int;
vi Cnt[maxm];
inline void Init(int N){
static int i,j,Now;
delete Visit; Visit=new int[N+1]();
delete Prime; Prime=new int[N+1]();
delete Num; Num=new int[N+1]();
for(Prime_Cnt=0,Cnt[1].push_back(1),i=2;i<=N;i++){
if(!Visit[i]) Prime[++Prime_Cnt]=i,Cnt[i].push_back(1),Num[i]=1,Visit[i]=i;
for(j=1;j<=Prime_Cnt && Prime[j]<=Visit[i] && Prime[j]<=N/i;j++){
Now=i*Prime[j],Visit[Now]=Prime[j];
Cnt[Now]=Cnt[i];
if(!(i%Prime[j])){
Cnt[Now][Cnt[Now].size()-1]+=1;
}else Cnt[Now].push_back(1);
}
}return;
}
}using namespace Count_Divisor_Number_Class;
int main(){
# ifdef LH_Frank
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
# endif
Init(1000000);
F[0][0]=1;
rep1(i,1,20) F[0][i]=2;
rep1(i,1,1000000){
Sum=0;
rep1(j,0,20){
Sum=(Sum+F[i-1][j])%Mod;
F[i][j]=Sum;
}
}
scanf("%d",&T);
while(T--){
scanf("%d%d",&R,&N);
Ans=1;
if(N==1){
printf("1\n");
continue;
}
repauto(Now,Cnt[N]){
Ans=Ans*F[R][Now]%Mod;
}printf("%lld\n",Ans);
}
return 0;
}