[NOI2016]bzoj 4652 循环之美 - 数论 - 杜教筛

首先题目是在求:

a n s = i = 1 n j = 1 m [ i j ] [ j k ]

注意到j和k互质来得简单,因为k只有2000并且是常数.
a n s = j = 1 m [ j k ] i = 1 n [ i j ]

a n s = j = 1 m [ j k ] i = 1 n d | i , d | j μ ( d )

a n s = d = 1 m i n ( n , m ) μ ( d ) n d d | j , j m [ j k ]

注意到若 j = t d 那么 [ j k ] = [ t d k ] = [ d k ] [ t k ]
a n s = d = 1 n μ ( d ) [ d k ] n d j = 1 m d [ j k ]

= d = 1 n μ ( d ) [ d k ] n d f ( m d )

其中 f ( n ) = i = 1 n [ i k ] = n k ϕ ( k ) + f ( n     m o d     k )
因此 f ( n ) 可以在 O ( k l g k ) O ( 1 ) 内解决
因此对最后的式子做数论分块,那么我们需要求:
g ( n , k ) = d = 1 n μ ( d ) [ d k ]
这个怎么做呢?设 n = p c q ,其中p是n的某个质因子,并且 p q
那么考虑 g ( n , q ) 中的所有数字,显然除了答案的部分还有和q互质但是不和p互质的部分要减去;
记这些中的数字为 y = t p , t n p , t q (显然因为p是质数所以不和p互质意味着是p的倍数)
考虑我们求的是这些 y μ 和,那么如果t不和p互质的话 μ ( y ) = 0 ,因此可以认为t也和p互质,因此t就和k互质。
g ( n , k ) = g ( n , q ) t = 1 n p [ t k ] μ ( t p )

注意到莫比乌斯函数是积性函数,而 t p ,因此 μ ( t p ) = μ ( t ) μ ( p )
g ( n , k ) = g ( n , q ) μ ( p ) t = 1 n p [ t k ] μ ( t )

注意到p是质数,所以 μ ( p ) = 1
g ( n , k ) = g ( n , q ) + t = 1 n p [ t k ] μ ( t ) = g ( n , q ) + g ( n p , k )

按照固定的顺序除以p,那么有效的状态只有 d ( k ) 2 ,其中 d ( k ) 表示k的因子个数,实现可以记忆化,因此只传k此时的质因子个数,会好些一些。
边界条件,当 n = 0 的时候 g ( n , k ) = 0 ,当k=1的时候需要杜教筛。综上总复杂度 O ( d ( k ) 2 + n 2 3 ) .

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#define lint long long
#define N 6000000
#define hv(n,c) (n*10000ll+c)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int ms[N],m[N],pri[N];bool np[N];
map<int,int> sav;
map<lint,int> gs;
int v[50],f[2010];
inline int prelude(int n)
{
    ms[1]=m[1]=1;
    for(int i=2,c=0;i<=n;i++)
    {
        if(!np[i]) pri[++c]=i,m[i]=-1;ms[i]=ms[i-1]+m[i];
        for(int j=1;j<=c&&(lint)i*pri[j]<=n;j++)
        {
            int x=i*pri[j];np[x]=true;
            if(i%pri[j]) m[x]=-m[i];
            else { m[x]=0;break; }
        }
    }
    return 0;
}
inline int S(int n)
{
    if(n<N) return ms[n];int ans=0ll;
    if(sav.count(n)) return sav[n];
    for(int s=2,t;s<=n;s=++t)
        t=n/(n/s),ans+=(t-s+1)*S(n/t);
    return sav[n]=1-ans;
}
inline int g(int n,int c)
{
    lint nc=hv(n,c);if(gs.count(nc)) return gs[nc];
    if(!n) return 0;if(!c) return gs[nc]=S(n);
    return gs[nc]=g(n,c-1)+g(n/v[c],c);
}
inline int g(int a,int b,int c) { return g(b,c)-g(a-1,c); }
inline int F(int n,int k) { return n<=k?f[n]:(n/k)*f[k]+f[n%k]; }
inline int gcd(int a,int b) { return a?gcd(b%a,a):b; }
int main()
{
    int n,m,k,c=0;scanf("%d%d%d",&n,&m,&k);
    prelude(N-1);lint ans=0ll;int ks=k;
    for(int i=2;i<=ks;i++) if(ks%i==0)
    {   v[++c]=i;while(ks%i==0) ks/=i;  }
    for(int i=1;i<=k;i++) f[i]=f[i-1]+(gcd(i,k)==1);
    for(int s=1,t;s<=min(n,m);s=++t)
        t=min(n/(n/s),m/(m/s)),ans+=(lint)F(m/s,k)*(n/t)*g(s,t,c);
    return !printf("%lld\n",ans);
}


猜你喜欢

转载自blog.csdn.net/mys_c_k/article/details/80834028