codeforces 235 E. Number Challenge(反演【d的定理)

题目链接

分析:

i=1aj=1bk=1cd(ijk) ∑ i = 1 a ∑ j = 1 b ∑ k = 1 c d ( i · j · k )

之前写过约数个数和,加强版

i=1aj=1bk=1cd(ijk)=i=1aj=1bk=1caibjck[gcd(i,j)=gcd(i,k)=gcd(j,k)=1] ∑ i = 1 a ∑ j = 1 b ∑ k = 1 c d ( i · j · k ) = ∑ i = 1 a ∑ j = 1 b ∑ k = 1 c a i b j c k [ g c d ( i , j ) = g c d ( i , k ) = g c d ( j , k ) = 1 ]

用反演把 [gcd(i,j)=1] [ g c d ( i , j ) = 1 ] 化开

i=1aj=1bk=1caibjckd|gcd(i,j)μ(d)[gcd(i,k)=gcd(j,k)=1] ∑ i = 1 a ∑ j = 1 b ∑ k = 1 c a i b j c k ∑ d | g c d ( i , j ) μ ( d ) [ g c d ( i , k ) = g c d ( j , k ) = 1 ]

=k=1cckd=1aμ(d)i=1a[d|i]ai[gcd(i,k)=1]j=1b[d|j]bj[gcd(j,k)=1] = ∑ k = 1 c c k ∑ d = 1 a μ ( d ) ∑ i = 1 a [ d | i ] a i [ g c d ( i , k ) = 1 ] ∑ j = 1 b [ d | j ] b j [ g c d ( j , k ) = 1 ]

=k=1cckd=1aμ(d)i=1a/dadi[gcd(i,k)=1]j=1b/dbdj[gcd(j,k)=1] = ∑ k = 1 c c k ∑ d = 1 a μ ( d ) ∑ i = 1 a / d a d i [ g c d ( i , k ) = 1 ] ∑ j = 1 b / d b d j [ g c d ( j , k ) = 1 ]

f(n,m)=ni=1ni[gcd(i,m)=1] f ( n , m ) = ∑ i = 1 n n i [ g c d ( i , m ) = 1 ]

=k=1cckd=1aμ(d)f(ad,k)f(bd,k) 原 式 = ∑ k = 1 c c k ∑ d = 1 a μ ( d ) f ( a d , k ) f ( b d , k )

f f 函数可以用 O(n) O ( n ) 的时间算出
总时间复杂度为 O(n2logn) O ( n 2 l o g n )

简单的小优化

预处理gcd
因为 a,b,c a , b , c 没有顺序,所以我们可以选择两个较小的枚举

tip

代码一开始以为会比较玄学,有分块之类的
实际上就是一个类似暴力的东西。。。

#include
#include
#include

using namespace std;

const int N=2005;
int a,b,c;
int mu[N],sshu[N],tot=0,gcd[N][N],f[N][N];
bool no[N];

int GCD(int a,int b){
    int r=a%b;
    while (r) {
        a=b;b=r;r=a%b;
    }
    return b;
}

void prepare() {
    mu[1]=1;
    for (int i=2;iif (!no[i]) {
            sshu[++tot]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=tot&&sshu[j]*ino[sshu[j]*i]=1;
            if (i%sshu[j]==0) {
                mu[i*sshu[j]]=0;
                break;
            }
            mu[i*sshu[j]]=-mu[i];
        }
    }

    for (int i=1;ifor (int j=i;jint F(int n,int m) {
    if (f[n][m]!=-1) return f[n][m];
    int ans=0;
    for (int i=1;i<=n;i++)
        if (gcd[i][m]==1) ans+=n/i;
    f[n][m]=ans;
    return ans;
}

int main()
{
    prepare();

    scanf("%d%d%d",&a,&b,&c);
    if (a>b) swap(a,b);
    if (b>c) swap(b,c);
    if (a>b) swap(a,b);

    memset(f,-1,sizeof(f));
    int ans=0;
    int last;
    for (int i=1;i<=a;i++)
        for (int j=1;j<=b;j++)
            if (gcd[i][j]==1) 
                ans+=mu[j]*(a/i)*F(b/j,i)*F(c/j,i);

    printf("%d\n",ans&((1<<30)-1));
    return 0;
}

你可能感兴趣的:(反演,反演)