JZPKIL题解

Description

F(N,X,Y)=Ni=1(i,N)X[i,N]Y
N,X,Y,F(N,X,Y)
数据范围 N1018,X,Y3000
有不超过100组询问
单点时限5秒

Solution

我们一开始肯定可以将式子先化简。
先将 [i,n] 化简。
F(N)=Ni=1(i,N)a(iN)b,where a=XY,b=Y
考虑枚举 (i,N)
F(N)=d|NdaNdi=1,(i,Nd)=1(idN)b
=Nbd|Nda+bNdi=1ibe((i,Nd))
其中 e(N) 为单位函数,当且仅当 N=1 时, e(N)=1,e(N)=0
并且我们有 e(N)=k|Nμ(k) —–(Mobius反演)
所以
F(N)=Nbd|Nda+bNdi=1ibk|i,k|Ndμ(k)
=Nbd|Nda+bk|Ndμ(k)kbNdki=1ib

然后可以注意到的是现在问题变为了
G(N)=Ni=1ik

要解决这个问题,我们需要引入一个东西叫做伯努利数 Bi
其定义为 Ni=0BiCiN+1=0

并且我们有
N1i=0ik=1k+1kj=0Cjk+1BjNk+1j

具体证明可以查一下wiki>.<

有了这个定理之后我们就可以尝试去继续化简上面的式子了。
F(N)=Nbd|Nda+bk|Ndμ(k)kb1b+1bj=0Cb+1jBj(Ndk)j
=Nb1b+1bj=0Cb+1jBjd|Nda+bk|Ndμ(k)kb(Ndk)b+1j
我们设一个函数
G(N,j)=d|Nda+bk|Ndμ(k)kb(Ndk)b+1j
那么 F(N)=Nb1b+1bj=0Cb+1jBjG(N,j)

因为 b3000 ,所以我们先考虑如何计算 G(N,j)

事实上 G(N,j) 是一个积性函数。

证明:?(狄利克雷卷积。比较好证)

N=ci=1ptii

由于 G(N,j) 是一个积性函数。所以我们事实上可以计算 G(ptii,j) 然后把他们乘起来就可以了。

而且对于 G(ptii,j)便

G(pt,j)=dt(pd)a+bktdμ(pk)(pk)b(ptdk)b+1j

又由 μ(n) 的定义,上式中只有当 k=01 的时候 μ(pk)0
所以 G(pt,j) 可以通过直接枚举 d 来计算。

那么分解完质因数之后计算 G(N,j) 的总时间复杂度就是 O(logN) 的了。

然而现在的问题是:怎么分解质因数??

事实上我们这题需要用到 pollard rho 分解方法。

简单的来说,这个算法的工作原理就是不断地随机两个数 X,Y,0X,Y<NXY N 是我们要分解的数。
然后设 P=gcd(|XY|,N) 很显然的 P 必然是 N 的约数。

并且由于生日悖论。我们期望下只需要 (P) 步就是随机到一个 N 的约数 P ,且 P>1 .
又由于较小的约数 PN12
所以一次只需要 O(N14)
那么总的时间复杂度就是 O(N14logN) 的了。
当然,在做之前我们需要先判断 N 是否为质数。 (miller rabin))

那么我们现在的任务就只剩下计算伯努利数 Bi

本题由于 X,Y3000 所以我们可以直接根据定义来 O(Y2)

那么总的时间复杂度就是 O(Y2+T(N14logN+YlogN))

然而事实上根据
xex1=i0Bixii!

我们可以通过多项式求逆元的方法 O(YlogY) 求出 B 的系数。

那么这题就可以做到
O(YlogY+T(N14logN+YlogN))

代码?

#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;
}

你可能感兴趣的:(JZPKIL题解)