链接:https://vjudge.net/problem/HDU-6286
题意:
给两段区间,问从这两段区间内各选出一个数,能组成多少个有序数对,使得数对里的两个元素乘积为2018的倍数。
思路:
首先注意两点,区间范围是1 ~ 1e9,n^2循环是不存在的了,而本题只能使用O(1)的复杂度;
其次要注意,<2018, 1> 和 <1, 2018> 显然是两个不同的有序对。
接下来讲下大体思路,刚才提到这题正解是O(1)的,那就说明肯定存在某些规律来算答案。
首先,2018这个数,Emmm,其实挺不错的,因为它就4个因子:1,2,1009,2018。
所以我的想法是,先看最大的因子所能决定的有序对个数,找下这两段区间分别有多少数是2018的倍数,然后再用其中一个区间2018倍数的个数乘另一个区间的长度再加和即可,即:
ll sum = 0;
//区间1内2018倍数个数
ll a1 = r / 2018 - l / 2018;
if(l % 2018 == 0) a1++;
//区间2内2018倍数个数
ll a2 = R / 2018 - L / 2018;
if(L % 2018 == 0) a2++;
//ps: 这里要提一下,在某段区间 [l, r] 内找某个数x的倍数个数tmp,不需要循环枚举,只需要两步:
int tmp = r / x - l / x; if(l % x == 0) tmp++;
sum += a1 * (R - L + 1) + a2 * (r - l + 1);
但是要注意这么做的话会有重复的情况,那就是再乘区间长的的时候,把之前找过的2018的倍数又覆盖到了,举个栗子~
第一个区间是1 ~ 2018,故第一个区间内2018倍数个数是1,即只有2018,第二个区间是 1~ 4036,故第二个区间内2018倍数个数是2,即2018和4036,而第一个区间找到的2018在乘区间长度的时候,会覆盖到2018(1) * 2018(2) 和 2018(1) * 4036(2),为了方便理解,这里加的括号(1) (2)表示属于哪段区间。所以此时,sum应减去这个重复值,即sum -= a1 * a2。
然后开始找1009的奇数倍,因为2018的倍数找过了,而2018的倍数都是1009的偶数倍,为避免重复,只需要找一段区间内1009的奇数倍再乘另一段区间内除了2018之外的偶数个数,在累加到sum上即可。而1009奇数倍的求法就用1009的倍数个数减去2018的倍数个数即可。
即进行如下操作:
//区间1内1009倍数个数
ll c1 = r / 1009 - l / 1009;
if(l % 1009 == 0) c1++;
//区间2内1009倍数个数
ll c2 = R / 1009 - L / 1009;
if(L % 1009 == 0) c2++;
ll t1 = c1 - a1; //区间1内1009奇数倍的个数
ll t2 = c2 - a2; //区间2内1009奇数倍数个数
//区间1内偶数个数
ll p1 = r / 2 - l / 2;
if(!(l & 1)) p1++;
//区间2内偶数个数
ll p2 = R / 2 - L / 2;
if(!(L & 1)) p2++;
sum += t1 * (p2 - a2) + t2 * (p1 - a1); //该区间1009奇数倍数个数 * 另一区间除2018倍数外偶数个数
本人AC代码://代码注释也很详细哦 ~
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <ctime>
#include <cctype>
#include <stack>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
const double PI = acos(-1.0);
queue <int> qua;
map <int, int> mp;
priority_queue < int, vector<int>, less<int> > qua1;
priority_queue < int, vector<int>, greater<int> > qua2;
ll l, r, L, R;
int main() {
while(scanf("%lld %lld %lld %lld", &l, &r, &L, &R) != EOF) {
ll sum = 0;
//区间1内2018倍数个数
ll a1 = r / 2018 - l / 2018;
if(l % 2018 == 0) a1++;
//区间2内2018倍数个数
ll a2 = R / 2018 - L / 2018;
if(L % 2018 == 0) a2++;
sum += a1 * (R - L + 1) + a2 * (r - l + 1) - a1 * a2; //减掉a1 * a2 是排除2018倍数 * 2018倍数重复情况
//区间1内1009倍数个数
ll c1 = r / 1009 - l / 1009;
if(l % 1009 == 0) c1++;
//区间2内1009倍数个数
ll c2 = R / 1009 - L / 1009;
if(L % 1009 == 0) c2++;
ll t1 = c1 - a1; //区间1内1009奇数倍的个数
ll t2 = c2 - a2; //区间2内1009奇数倍数个数
//区间1内偶数个数
ll p1 = r / 2 - l / 2;
if(!(l & 1)) p1++;
//区间2内偶数个数
ll p2 = R / 2 - L / 2;
if(!(L & 1)) p2++;
sum += t1 * (p2 - a2) + t2 * (p1 - a1); //该区间1009奇数倍数个数 * 另一区间除2018倍数外偶数个数
cout << sum << endl;
}
return 0;
}