欧拉函数,容斥定理
欧拉函数:
欧拉函数是少于或等于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;
}
浙公网安备 33010602011771号