定义F(N,X,Y)=∑Ni=1(i,N)X∗[i,N]Y
给定三个自然数N,X,Y,让你求出F(N,X,Y)
数据范围 N≤1018,X,Y≤3000
有不超过100组询问
单点时限5秒
我们一开始肯定可以将式子先化简。
先将 [i,n] 化简。
F(N)=∑Ni=1(i,N)a∗(i∗N)b,where a=X−Y,b=Y
考虑枚举 (i,N)
F(N)=∑d|Nda∑Ndi=1,(i,Nd)=1(i∗d∗N)b
=Nb∑d|Nda+b∑Ndi=1ib∗e((i,Nd))
其中 e(N) 为单位函数,当且仅当 N=1 时, e(N)=1,否则e(N)=0
并且我们有 e(N)=∑k|Nμ(k) —–(Mobius反演)
所以
F(N)=Nb∑d|Nda+b∑Ndi=1ib∗∑k|i,k|Ndμ(k)
=Nb∑d|Nda+b∑k|Ndμ(k)∗kb∑Nd∗ki=1ib
然后可以注意到的是现在问题变为了
求 G(N)=∑Ni=1ik
要解决这个问题,我们需要引入一个东西叫做伯努利数 Bi
其定义为 ∑Ni=0Bi∗CiN+1=0
并且我们有
∑N−1i=0ik=1k+1∑kj=0Cjk+1BjNk+1−j
具体证明可以查一下wiki>.<
有了这个定理之后我们就可以尝试去继续化简上面的式子了。
F(N)=Nb∑d|Nda+b∑k|Ndμ(k)∗kb1b+1∑bj=0Cb+1jBj(Nd∗k)j
=Nb1b+1∑bj=0Cb+1jBj∑d|Nda+b∑k|Ndμ(k)∗kb∗(Nd∗k)b+1−j
我们设一个函数
G(N,j)=∑d|Nda+b∑k|Ndμ(k)∗kb∗(Nd∗k)b+1−j
那么 F(N)=Nb∗1b+1∑bj=0Cb+1j∗Bj∗G(N,j)
因为 b≤3000 ,所以我们先考虑如何计算 G(N,j)
事实上 G(N,j) 是一个积性函数。
证明:?(狄利克雷卷积。比较好证)
设 N=∏ci=1ptii
由于 G(N,j) 是一个积性函数。所以我们事实上可以计算 G(ptii,j) 然后把他们乘起来就可以了。
而且对于 G(ptii,j)的计算是非常方便的。
G(pt,j)=∑d≤t(pd)a+b∑k≤t−dμ(pk)∗(pk)b∗(pt−d−k)b+1−j
又由 μ(n) 的定义,上式中只有当 k=0或1 的时候 μ(pk)≠0
所以 G(pt,j) 可以通过直接枚举 d 来计算。
那么分解完质因数之后计算 G(N,j) 的总时间复杂度就是 O(logN) 的了。
然而现在的问题是:怎么分解质因数??
事实上我们这题需要用到 pollard rho 分解方法。
简单的来说,这个算法的工作原理就是不断地随机两个数 X,Y,0≤X,Y<N,X≠Y , N 是我们要分解的数。
然后设 P=gcd(|X−Y|,N) 很显然的 P 必然是 N 的约数。
并且由于生日悖论。我们期望下只需要 (√P) 步就是随机到一个 N 的约数 P ,且 P>1 .
又由于较小的约数 P≤N12
所以一次只需要 O(N14)
那么总的时间复杂度就是 O(N14logN) 的了。
当然,在做之前我们需要先判断 N 是否为质数。 ((miller rabin))
那么我们现在的任务就只剩下计算伯努利数 Bi
本题由于 X,Y≤3000 所以我们可以直接根据定义来 O(Y2)来计算
那么总的时间复杂度就是 O(Y2+T∗(N14logN+Y∗logN))
然而事实上根据
xex−1=∑i≥0Bi∗xii!
我们可以通过多项式求逆元的方法 O(YlogY) 求出 B 的系数。
那么这题就可以做到
O(YlogY+T∗(N14logN+Y∗logN))
代码?
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 65,MAXM = 3005,Mo = int(1e9) + 7,G = 5959741;
const int Sta[9] = {2,3,5,7,11,13,17,19,23};
typedef long long LL;
typedef long double ld;
LL N,Fac[MAXN],Pw[25][65],Pt[25][65],Pl[25],Rv[25],Rvt[25],Tr[25],SEAD;
LL All[25],Fi[25];
int Ti[MAXN],C[MAXM][MAXM],B[MAXM],X,Y,cnt,c,c1;
LL Mul(LL a,LL b,LL c)
{
a %= c;b %= c;
return (a * b - LL(ld(a) / c * b + 1e-3) * c + c) % c;
}
LL gcd(LL a,LL b)
{
return b ? gcd(b,a % b) : a;
}
LL Pow(LL a,LL b,LL mo)
{
a %= mo;
LL tmp = 1;
for(;b;b >>= 1,a = LL(a) * a % mo) if (b & 1) tmp = tmp * LL(a) % mo;
return tmp;
}
LL Pow1(LL a,LL b,LL mo)
{
LL tmp = 1;
for(;b;b >>= 1,a = Mul(a,a,mo)) if (b & 1) tmp = Mul(tmp,a,mo);
return tmp;
}
LL Pow(LL a,LL b)
{
return Pow(a,b,Mo);
}
void Berno()
{
int M = 3001;
for(int i = 1;i <= M;i ++) C[i][0] = C[0][i] = 1;
for(int i = 1;i <= M;i ++)
for(int j = 1;j <= i;j ++)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % Mo;
B[0] = 1;
for(int i = 1;i <= M;i ++)
{
int tmp = i + 1;
for(int j = 0;j < i;j ++) tmp = (tmp - B[j] * 1ll * C[i + 1][j] % Mo + Mo) % Mo;
B[i] = tmp * Pow(i + 1,Mo - 2,Mo) % Mo;
c1 ++;
}
}
bool Test(LL N)
{
LL P = N;
N --;
int t = 0;
for(;!(N & 1);N >>= 1,t ++);
for(int i = 0;i < 9;i ++)
{
if (Sta[i] >= N) break;
LL cur;
if (cur = Pow1(Sta[i],N,P),cur == 1 || cur == (P - 1)) continue;
bool ok = 0;
for(int i = 1;i <= t;i ++)
{
cur = Mul(cur,cur,P);
if (cur == (P - 1)) {ok = 1;break;}
}
if (!ok) return 0;
}
return 1;
}
void Add(LL ft)
{
for(int i = 1;i <= cnt;i ++) if (Fac[i] == ft) {Ti[i] ++;return;}
Fac[++ cnt] = ft,Ti[cnt] = 1;
}
bool Prime(LL N)
{
if (N <= 1000000) return 1;
return Test(N);
}
int RAND()
{
SEAD = (SEAD * 3 + G);
while (SEAD >= 998244353) SEAD -= 998244353;
return SEAD;
}
void Rho(LL N)
{
if (N == 1) return;
if (Prime(N)) {Add(N);return;}
while (1)
{
LL X = 2,Y = 2,P = 1;
int C = RAND();
while (P == 1)
{
X = (Mul(X,X,N) + C) % N;
Y = (Mul(Y,Y,N) + C) % N;
Y = (Mul(Y,Y,N) + C) % N;
P = gcd(abs(X - Y),N);
}
if (P != N)
{
Rho(N / P),Rho(P);
break;
}
}
}
void Factor(LL N)
{
for(int i = 2;i <= 1000;i ++)
if (N % i == 0)
{
Fac[++ cnt] = i,Ti[cnt] = 0;
for(;N % i == 0;N /= i) Ti[cnt] ++;
}
Rho(N);
}
int miu(int n)
{
if (n == 1 || n == 6) return 1;
if (n == 3 || n == 2) return -1;
return 0;
}
void Work()
{
cnt = 0;
scanf("%lld%d%d", &N, &X, &Y);
Factor(N);
LL ans = 0;
for(int i = 1;i <= cnt;i ++)
{
LL p = Fac[i];
int k = Ti[i];
Pl[i] = Pow(p,X);
Pw[i][0] = Pt[i][0] = 1;
for(int l = 1;l <= k;l ++) Pw[i][l] = Pw[i][l - 1] * p,Pt[i][l] = Pt[i][l - 1] * Pl[i] % Mo;
Rv[i] = Pow(Pow(p,Y + 1),Mo - 2),Tr[i] = Pow(p,Y);
Rvt[i] = Pow(Pw[i][k],Mo - 2);
Fi[i] = Pow(Pw[i][k],Y + 1);
All[i] = Pow(Pw[i][k],Y);
}
for(int i = 0;i <= Y;i ++)
{
int tmp = 0;
LL all = 1;
for(int j = 1;j <= cnt;j ++)
{
LL cur = 0;
int k = Ti[j];
for(int l = 0,fi = Fi[j];l <= k;l ++)
{
LL co = Pt[j][l],lc = 0;
lc = fi;
if (k - l) lc = (lc - Tr[j] * 1ll * fi % Mo * Rv[j] % Mo + Mo) % Mo;
cur = (cur + co * lc) % Mo;
fi = fi * Rv[j] % Mo;
}
all = all * cur % Mo * All[j] % Mo;
Fi[j] = Fi[j] * Rvt[j] % Mo;
Rv[j] = Rv[j] * (Fac[j] % Mo) % Mo;
}
ans = (ans + all * C[Y + 1][i] % Mo * B[i] % Mo) % Mo;
}
ans = ans * Pow(Y + 1,Mo - 2) % Mo;
printf("%d\n", ans);
}
int main()
{
cnt = 0;
SEAD = int(1e7) + 19;
Berno();
int T;
for(scanf("%d", &T);T;T --) Work();
return 0;
}