欧拉函数,容斥定理

hdoj 1695 GCD

欧拉函数: 

欧拉函数是少于或等于n的数中与n互质的数的数目.

欧拉函数和它本身不同质因数的关系:欧拉函数ψ(N)=N{∏p|N}(1-1/p)。(P是数N的质因数)

如:
  ψ(10)=10×(1-1/2)×(1-1/5)=4;
  ψ(30)=30×(1-1/2)×(1-1/3)×(1-1/5)=8;
  ψ(49)=49×(1-1/7)=42。

可以用筛选法求出欧拉函数。

memset( euler, 0, sizeof(euler) );
euler[1] = 1;
for( i=2; i<maxn; ++i ) 
{
	 if( euler[i] ) continue;
	 for( j=i; j<maxn; j+=i ) 
	 {
		if( !euler[j] ) euler[j] = j;
		euler[j] = euler[j]*(i-1)/i;
	 }		
}

容斥原理:

在计数时,必须注意无一重复,无一遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。

网上看到用dfs来实现,写的比较简单,但还不是很懂。蛋疼。。。

hdoj 1695代码:

#include <iostream>
#include <vector>
using namespace std;

typedef __int64 lld;
const int maxn = 100005;
int a, b, c, d, k;
lld euler[maxn];  //存放对应的欧拉函数值
vector<int> vec[maxn];  //存放每个数的质因子


void init() 
{
    int i, j;
    for( i=0; i<maxn; ++i ) vec[i].clear();
    memset( euler, 0, sizeof(euler) );
    euler[1] = 1;
    for( i=2; i<maxn; ++i ) 
    {
        if( !euler[i] ) 
        {
            for( j=i; j<maxn; j+=i ) 
            {
                if( !euler[j] ) euler[j] = j;
                euler[j] = euler[j]*(i-1)/i;
                vec[j].push_back( i );
            }    
        }
        euler[i] += euler[i-1];
    }
}



lld dfs( int idx, int small, int big ) 
{
    lld sum = 0;
    for(int i=idx; i<vec[big].size(); ++i ) 
        sum += small/vec[big][i] - dfs( i+1, small/vec[big][i], big );
    return sum;
}



lld solve() 
{
    int i, j, x, y;
    lld ans = 0;
    if( k == 0 ) return 0;
    if( b > d ) swap( b, d );
    x = b / k;
    y = d / k;
    ans = euler[x];
    for( i=x+1; i<=y; ++i ) 
        ans += x - dfs( 0, x, i );
    return ans;
}




int main() 
{
//    freopen( "c:/aaa.txt", "r", stdin );
    int T, ca, x, y;
    init();
    scanf( "%d", &T );
    for( ca=1; ca<=T; ++ca ) 
    {
        scanf( "%d %d %d %d %d", &a, &b, &c, &d, &k );
        printf( "Case %d: %I64d\n", ca, solve() );
    }
    return 0;
}

hdoj 1796 How many integers can you find 就是容斥,做完这题基本理解dfs对容斥的实现了,代码如下:

sum=(n/a[1]+n/a[2]...)-(n/(a[1]*a[2])+n/(a[1]*a[3])..)-(...)  而用容斥时,满足a[1],a[2]...互质。所以他们每项相乘作为分母前,必须排除公因子。

#include <iostream>
using namespace std;

int n, m, num[15];
int cnt;


int gcd( int a, int b ) {
	if( a < b ) swap( a, b );
	return ( b == 0 ) ? a : gcd( b, a%b );
}


void dfs( int idx, int len, int sum, int deep ) {
	if( deep + m - idx + 1 < len ) return; //如果总长度小了就不用往下dfs了
	if( deep == len && sum != 0 ) {
		cnt += (n - 1) / sum;
		return ;
	}

	for( int i=idx; i<=m; ++i ) {
		int temp = num[i] / gcd( sum, num[i] );
		sum *= temp;
		dfs( i+1, len, sum, deep+1 );
		sum /= temp;
	}
}


void solve() {
	int i, j, ans;
	ans = 0;
	for( i=1; i<=m; ++i ) {
		cnt = 0;
		dfs( 1, i, 1, 0 );
		ans += (i&1) ? cnt : -cnt;
	}
	printf( "%d\n", ans );
}


int main() {
//	freopen( "c:/aaa.txt", "r", stdin);
	while( scanf("%d %d", &n, &m) == 2 ) {
		int t = 0;
		for( int i=1; i<=m; ++i ) {
			scanf( "%d", &num[i] );
			if( num[i] != 0 ) num[++t] = num[i];
		}
		m = t;
		solve();
	}
	return 0;
}
	

 由此,如果hdoj 1695 中用上面这种比较好理解的dfs方法来实现的话,我想应该就是这样

代码:

#include <iostream>
#include <vector>
using namespace std;


typedef __int64 lld;
const int maxn = 100005;
int a, b, c, d, k, n, m, l;
lld euler[maxn], cnt;  //存放对应的欧拉函数值
vector<int> vec[maxn];  //存放每个数的质因子


void init() 
{
    int i, j;
    for( i=0; i<maxn; ++i ) vec[i].clear();
    memset( euler, 0, sizeof(euler) );
    euler[1] = 1;
    for( i=2; i<maxn; ++i ) 
    {
        if( !euler[i] ) 
        {
            for( j=i; j<maxn; j+=i ) 
            {
                if( !euler[j] ) euler[j] = j;
                euler[j] = euler[j]*(i-1)/i;
                vec[j].push_back( i );
            }    
        }
        euler[i] += euler[i-1];
    }
}


/*
lld dfs( int idx, int small, int big ) 
{
    lld sum = 0;
    for(int i=idx; i<vec[big].size(); ++i ) 
        sum += small/vec[big][i] - dfs( i+1, small/vec[big][i], big );
    return sum;
}
*/


void dfs( int idx, int len, lld sum, int deep ) 
{
	if( deep + m - idx < len ) return; 
	if( deep == len && sum != 0 ) {
		cnt += n / sum;
		return;
	}
	for( int i=idx; i<m; ++i ) {
		sum *= vec[l][i];
		dfs( i+1, len, sum, deep+1 );
		sum /= vec[l][i];
	}
}


lld rongchi( int small, int big ) //求小于small,和big不互质的数的个数
{
	int i;
	lld ans = 0;
	m = vec[big].size();
	n = small;
	l = big;
	for( i=0; i<m; ++i ) 
	{
		cnt = 0;
		dfs( 0, i+1, 1, 0 );
		ans += (i&1) ? -cnt : cnt;
	}
	return ans;
}


lld solve() 
{
    int i, j, x, y;
    lld ans = 0;
    if( k == 0 ) return 0;
    if( b > d ) swap( b, d );
    x = b / k;
    y = d / k;
    ans = euler[x];
    for( i=x+1; i<=y; ++i ) 
        ans += x - rongchi( x, i );
    return ans;
}



int main() 
{
//    freopen( "c:/aaa.txt", "r", stdin );
    int T, ca, x, y;
    init();
    scanf( "%d", &T );
    for( ca=1; ca<=T; ++ca ) 
    {
        scanf( "%d %d %d %d %d", &a, &b, &c, &d, &k );
        printf( "Case %d: %I64d\n", ca, solve() );
    }
    return 0;
}

posted on 2011-02-27 19:44  CrazyAC  阅读(917)  评论(0编辑  收藏  举报