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; }