本来是没有写博客的习惯,结果今天回头看自己A过的题,发现根本看不懂代码了,看着题目也完全没有思路,这就比较尬了,因此打算走上这条不归路。
言归正传,题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1803
看到题目,我的第一感觉就是分解因数即2016=1*2016=2*1008=…………对2016分解一下质因数就知道有多少种了,2016 = 2^5 * 3^2 * 7,额,这个手动算起来有些麻烦。而且还有一点,题目要求a*b是2016倍数的数对个数,假设c*d=2016,那么(xc,yd)都满足条件了。这样我们只要求出(c,d)对应的x,y个数就好显然x <= m/c, y <= n/d。这就简单了,开两个数组,储存c,d对应的x,y就好,而对于c,d,一层for循环遍历一遍即可。求出来对应的x,y后又有一个麻烦的问题——判重,显然,2016的倍数也是1008的倍数,所以由容斥原理,我们可以将1008的倍数个数减去一个2016的倍数个数。那么继续算下去的时候,算到504的时候,发现要算出是504的倍数,但不是1008的倍数,也不是2016的倍数(没错,2016是1008的倍数,但我们之前已经减掉了,所以我们存的1008的倍数的个数实际上应该是是1008的倍数,而不是2016的倍数的个数),这个处理再用一层for循环处理就好了,然后最后用两层for循环遍历出i*j==2016的i和j,然后将i的倍数个数乘以j的倍数个数,最后加到计数器上即可。
最后代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> using namespace std; typedef long long LL; int main() { // freopen("in.txt", "r", stdin); LL m, n; while(~scanf("%lld%lld", &m, &n)) { LL M[2017] = {0}, N[2017] = {0}; for(int i = 2016; i; -- i) { M[i] = m/i; N[i] = n/i; for(int j = i+1; j < 2017; ++ j) if(j % i == 0 && 2016 % i == 0 && (2016/i) % (j/i) == 0) N[i] -= N[j]; } LL ans = 0; for(int i = 1; i < 2017; ++ i) for(int j = 1; j < 2017; ++ j) if(i * j == 2016) ans += M[i]*N[j]; printf("%lld\n", ans); }
return 0; }