欧拉函数,容斥定理
欧拉函数:
欧拉函数是少于或等于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; }