【HDU】1695 GCD 欧拉函数+容斥原理

来源:互联网 发布:怎么注册淘宝邮箱 编辑:程序博客网 时间:2024/06/11 20:55

传送门:【HDU】1695 GCD


题目分析:首先,如果要求数x与区间[ 1 , n ]的数互质的对数,那么,我们可以转化成先求x的所有质因数,对x的一个质因数a,在[ 1 , n ]中以a为约数的个数共有n/a个,令A = n/a,同里得到B,C,D等。则ans = n - A - B - C - D,但是这样是不是就好了呢?显然不是,因为各质因数的倍数是可能重复的,所以要加上A∩B,B∩C,C∩D,A∩C等。以此类推,我们减去奇数个的交集,加上偶数个的交集,得到的就是最终的ans。

本题,对于所给的b,d我们除以k,即b/=k,d/=k(以后用到的b,d也是除以k以后的),这样本题就转化成求[ 1 , b ],[ 1 , d ]内分别取两个数互质的对数(题目中申明(1,2)与(2,1)是本质相同的,算为一个)。我们为了快速得到所有数的质因数,可以用筛选法求得,同时还可以得到每个数的欧拉函数。并且本题有一点可以略微优化:首先我们默认b<=d,然后[ 1 , b ] , [1 , b ]中的对数就是欧拉函数1~b的前缀和。接下来[ 1 , b ] , [ b + 1 , d ]就暴力枚举[ b + 1 , d ]内的数然后用一开始的方法求解即可,得到交集可以用二进制枚举的方法,并且属于一个数的质因数最多不会超过八个,所以是绰绰有余了。


代码如下:


#include <cmath>#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;#define REP( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )#define REV( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )#define CLR( a , x ) memset ( a , x , sizeof a )typedef long long LL ;const int MAXN = 100005 ;int phi[MAXN] ;LL sum[MAXN] ;int num[MAXN][10] , cnt[MAXN] ;void fun () {CLR ( cnt , 0 ) ;for ( int i = 1 ; i < MAXN ; ++ i ) phi[i] = i ;for ( int i = 2 ; i < MAXN ; ++ i ) if ( phi[i] == i ) {for ( int j = i ; j < MAXN ; j += i ) {phi[j] = phi[j] / i * ( i - 1 ) ;num[j][cnt[j] ++] = i ;}}REP ( i , 1 , MAXN ) sum[i] = sum[i - 1] + phi[i] ;}void solve () {LL ans = 0 ;int a , b , c , d , K ;scanf ( "%d%d%d%d%d" , &a , &b , &c , &d , &K ) ;if ( K == 0 ) {printf ( "0\n" ) ;return ;}b /= K , d /= K ;if ( b > d ) swap ( b , d ) ;ans = sum[b] ;FOR ( i , b + 1 , d ) {ans += b ;int tot = 1 << cnt[i] ;REP ( j , 1 , tot ) {int count = 0 , tmp = 1 ;REP ( k , 0 , cnt[i] ) if ( j & ( 1 << k ) ) {++ count ;tmp *= num[i][k] ;}if ( count & 1 ) ans -= b / tmp ;else ans += b / tmp ;}}printf ( "%I64d\n" , ans ) ;}int main () {int T , cas = 0 ;fun () ;scanf ( "%d" , &T ) ;while ( T -- ) {printf ( "Case %d: " , ++ cas ) ;solve () ;}return 0 ;}


0 0