【数论+容斥】POJ 1091 跳蚤

KIDx的解题报告

 

题目链接:http://poj.org/problem?id=1091

 

假设卡片上标号分别是a1, a2, ..., an, M,跳蚤跳对应号的次数分别为x1, x2, ..., xn,跳M个单位长度的次数是xn+1,那么要满足已知条件只需满足方程:

a1x1+a2x2+...+anxn+Mxn+1 = 1 有解,即:

gcd (a1, a2, ..., an, M) = 1,接下来对M进行素因子分解,然后排除公因子非1的情况即可。

 

如代码所示:

设g为公因子非1的情况数,f(i)表示有i个公因子的情况数,由容斥原理得:g = f(1) - f(2) + f(3) -... f(k)

 

#include <iostream>
using namespace std;
#define LL __int64

int fac[35], k, a[20], n, m, x;
LL tp;

LL my_pow (LL a, int b)		//计算a^b
{
	LL res = 1;
	while (b--) res *= a;
	return res;
}

void dfs (int pos, int cnt, int num)	//dfs得到卡片中n+1个数有num个公因子时的方法数
{
	if (cnt == num)
	{
		x = m;
		for (int i = 0; i < cnt; i++)
			x /= a[i];					//x/p表示[1,x]中有多少个数是p的倍数
		tp += my_pow (x, n);			//要选n个数,每个数有x种选择
		return ;
	}
	for (int i = pos; i < k; i++)
	{
		a[cnt] = fac[i];
		dfs (i+1, cnt+1, num);
	}
}

void divide (int p)						//分解素因子,fac存放p的所有素因子
{
	for (int i = 2; i * i <= p; i++)
	{
		if (p % i == 0)
		{
			fac[k++] = i;
			p /= i;
			while (p % i == 0)
				p /= i;
		}
	}
	if (p > 1) fac[k++] = p;
}

int main()
{
	LL ans, g;
	int i;
	while (cin >> n >> m)
	{
		g = 0;
		divide (m);
		for (i = 1; i <= k; i++)			//g = f(1)-f(2)+f(3)-f(4)+...f(k)
		{
			tp = 0;
			dfs (0, 0, i);
			if (i & 1) g += tp;
			else g -= tp;
		}
		ans = my_pow (m, n) - g;		 //ans = m^n - g
		cout << ans << endl;
	}
	return 0;
}

 

你可能感兴趣的:(编程,C++,算法,ACM,KIDx)