题目链接:
http://acm.timus.ru/problem.aspx?space=1&num=1091
题目大意:
给你两个整数K和S,从小于等于S的非负整数中选择K个数,并且K个数的最大公约数大于1,
问总共有多少组。(2 <= K <= S <= 50)。
解题思路:
因为 2 <= K <= S <= 50,我们可以直接枚举质因数,求出从每个质因数的倍数中选择k个数
的组合数,累加起来即为方案个数,但是这样重复计算了很多情况。
例如:S = 20,K = 2。
2的倍数:2、4、6、8、10、12、14、16、18、20
3的倍数:3、6、9、12、15、18
5的倍数:5、10、15、20
7的倍数:7、14
11的倍数:11
……
如果单纯累加的话,很多数都重复计算了多次。应该去掉重复多加的部分。相当于把2的倍数、
3的倍数、…全部看作一个个的集合,求每部分的组合情况,然后求集合的并集。
如果2的倍数集合中选取K的数的方案数为P(2),所求结果为:
P(2)+P(3)+…+P(23)-P(2*3)-P(2*5)....(2*11) + P(2*3*5) + ……
因为S最大数不超过50,K最小数为2,则2*29 = 54 > 50了,所以质因数枚举到23即可。
求集合的并集利用容斥原理来做。
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; int Prime[22] = {2,3,5,7,11,13,17,19,23,29}; int C(int n,int m) { if(n < m) //不加就错 return 0; m = min(m, n-m); int ans = 1; for(int i = 1; i <= m; ++i) { ans *= (n-i+1); ans /= i; } return ans; } int K,S; int Solve() { int ans = 0,top = 0; for(int i = 0; i < 10; ++i) { if(S / Prime[i] < K) { top = i; break; } } for(int i = 1; i < (1 << top); ++i) { int odd = 0,Mult = 1; for(int j = 0; j < top; ++j) { if((1 << j) & i) { odd++; Mult *= Prime[j]; } } if(odd & 1) ans += C(S / Mult, K); else ans -= C(S / Mult, K); } if(ans <= 10000) return ans; else return 10000; } int main() { while(~scanf("%d%d",&K,&S)) { printf("%d\n",Solve()); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-09 13:53:09