链接:http://acm.hdu.edu.cn/showproblem.php?pid=1796
Time Limit: 12000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5612 Accepted Submission(s): 1608
For each case, output the number.
12 2 2 3
7
#include <iostream> #include <cstdio> #include <string> #include <cmath> #include <iomanip> #include <ctime> #include <climits> #include <cstdlib> #include <cstring> #include <algorithm> #include <queue> #include <vector> #include <set> #include <map> //#pragma comment(linker, "/STACK:102400000, 102400000") using namespace std; typedef unsigned int li; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const double pi = acos(-1.0); const double e = exp(1.0); const double eps = 1e-8; const int maxm = 15; // 集合最大计数 int num[maxm]; // m个数的集合 int n, m, cnt; // 给定数n,集合计数m,不为零的m集合计数 int ans; // 最后所求结果 int gcd(int a, int b); // 求最大公约数 int lcm(int a, int b); // 求最小公倍数 void dfs(int index, int common, int sign); // 利用深搜展现容斥原理 int main() { ios::sync_with_stdio(false); while (~scanf("%d%d", &n, &m)) { int x; // 集合元素 cnt = 0; while (m--) { scanf("%d", &x); if (0 == x) // 集合元素可能含有零,丢弃零 continue; num[cnt++] = x; // 不为零的元素存入集合 } ans = 0; // 初始化最后所求结果 for (int i=0; i<cnt; i++) dfs(i, num[i], 1); // 枚举m集合中非零元素进行深搜 printf("%d\n", ans); } return 0; } int gcd(int a, int b) { // 欧几里得算法/辗转相除法 return b ? gcd(b, a%b) : a; } int lcm(int a, int b) { // 这样写,有利于防止中间过程溢出 return a/gcd(a, b)*b; } void dfs(int index, int common, int sign) { // 集合元素的索引,最小公倍数,符号判断(容斥公式中的奇数项加偶数项减) common = lcm(num[index], common); if (sign & 1) // 奇数项加 ans += (n-1)/common; // 小于n的数,所以用n-1 else // 偶数项减 ans -= (n-1)/common; for (int i=index+1; i<cnt; ++i) dfs(i, common, sign+1); // 一层是找出一个元素的子集最小公倍数的计数 // 两层是找出两个元素的子集最小公倍数的计数 // 以此类推,所有子集最小公倍数的计数都找出 }