[BZOJ3512]DZY Loves Math IV 杜教筛+记忆化搜索

发现 n n 只有 =105 = 10 5 ,我们先考虑 S(n,m)=mi=1φ(ni) S ( n , m ) = ∑ i = 1 m φ ( n i ) 怎么求。
先把 n n 分成两部分, n1=pi n 1 = ∏ p i ,即所有质因子一次幂的乘积, n2=pci1i n 2 = ∏ p i c i − 1 ,即剩下的。不难发现 φ(n)=φ(n1)n2 φ ( n ) = φ ( n 1 ) ∗ n 2

S(n,m)=n2i=1mφ(n1i)=n2i=1mφ(n1)φ(i)gcd(n1,i)φ(gcd(n1,i)) S ( n , m ) = n 2 ∑ i = 1 m φ ( n 1 i ) = n 2 ∑ i = 1 m φ ( n 1 ) φ ( i ) gcd ( n 1 , i ) φ ( gcd ( n 1 , i ) )

然后因为 φ φ 是积性函数,所以当 gcd(b,a/b)=1 gcd ( b , a / b ) = 1 时, φ(a)φ(b)=φ(ab) φ ( a ) φ ( b ) = φ ( a b ) ,所以
=n2i=1mφ(n1gcd(n1,i))φ(i)gcd(n1,i)=n2i=1mφ(n1gcd(n1,i))φ(i)d|gcd(n1,i)φ(d) = n 2 ∑ i = 1 m φ ( n 1 gcd ( n 1 , i ) ) φ ( i ) gcd ( n 1 , i ) = n 2 ∑ i = 1 m φ ( n 1 gcd ( n 1 , i ) ) φ ( i ) ∑ d | gcd ( n 1 , i ) φ ( d )

这里用 d|nφ(d) ∑ d | n φ ( d ) 代替 n n 是为了去掉 gcd gcd 的限制,并且和前面的合并。
=n2i=1mφ(i)d|gcd(n1,i)φ(n1d)=n2d|n1φ(n1d)i=1mdφ(di)=n2d|n1φ(n1d)S(d,md) = n 2 ∑ i = 1 m φ ( i ) ∑ d | gcd ( n 1 , i ) φ ( n 1 d ) = n 2 ∑ d | n 1 φ ( n 1 d ) ∑ i = 1 ⌊ m d ⌋ φ ( d i ) = n 2 ∑ d | n 1 φ ( n 1 d ) S ( d , m d )

注意到 S S 中的参数最多只有 nm n m 种,可以记忆化,当 n=1 n = 1 的时候就是杜教筛啦。
听说复杂度 O(nm+m23) O ( n m + m 2 3 ) 。可是还是不太明白求了所有的 mdi=1φ(i) ∑ i = 1 m d φ ( i ) 为什么还是 O(m23) O ( m 2 3 ) 。。。
代码:

#include
#include
#include
#include
#include
#include
#include
#define MP make_pair
#define PB push_back
#define fs first
#define sc second
#define ll long long
#define N 100010
#define U 6000000
#define P 1000000
#define ID(x,y) (x*1234567891+y)
#define up(x,y) x=(x+(y))%mod
using namespace std;
const int mod=1000000007;
bool flag[U+5];
int n,m,pri[U+5],minp[U+5],num,sp[U+5],phi[U+5];
struct hash
{
    vector > a[P];
    void ins(ll x,ll y)
    {
        int d=x%P;
        a[d].PB(MP(x,y));
    }
    ll qry(ll x)
    {
        int d=x%P;
        for(int i=a[d].size()-1;i>=0;i--)
            if(a[d][i].fs==x) return a[d][i].sc;
        return -1;   
    }
}h;
void getpri()
{
    memset(flag,1,sizeof(flag));
    flag[1]=0;phi[1]=1;
    for(int i=2;i<=U;i++)
    {
        if(flag[i]) pri[++num]=i,phi[i]=i-1,minp[i]=i;
        for(int j=1;j<=num&&i*pri[j]<=U;j++)
        {
            int v=i*pri[j];
            flag[v]=0;minp[v]=pri[j];
            if(i%pri[j]==0) {phi[v]=phi[i]*pri[j];break;}
            phi[v]=phi[i]*phi[pri[j]];          
        }
    }
}
void fj(int x,int *t)
{
    while(x>1)
    {
        t[++t[0]]=minp[x];
        while(x%t[t[0]]==0) x/=t[t[0]];
    }
}
ll qphi(int x)
{
    if(x<=U) return sp[x];
    ll re=((ll)x*(x+1)/2)%mod;
    for(int l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        up(re,mod-qphi(x/l)*(r-l+1)%mod);
    }
    return re;
}
ll S(ll a,ll b)
{
    if(!b) return 0;
    if(h.qry(ID(a,b))!=-1) return h.qry(ID(a,b));
    if(a==1)
    {
        h.ins(ID(a,b),qphi(b));
        return h.qry(ID(a,b));
    }
    int t[12],w=1;
    ll re=0;
    t[0]=0;
    fj(a,t);
    for(int i=1;i<=t[0];i++)
        w*=t[i];
    for(int i=floor(sqrt(w));i;i--)
        if(w%i==0) up(re,S(i,b/i)*phi[w/i]),up(re,S(w/i,b/(w/i))*phi[i]);
    re=re*a/w%mod;
    h.ins(ID(a,b),re);
    return re;  
}
int main()
{
    getpri();
    for(int i=1;i<=U;i++)
        sp[i]=(phi[i]+sp[i-1])%mod; 
    scanf("%d%d",&n,&m);
    ll ans=0;
    for(int i=1;i<=n;i++)
        up(ans,S(i,m));
    printf("%lld",ans);
    return 0;
}

你可能感兴趣的:(dp,数论,杜教筛)