1. 程式人生 > >6286 CCPC 2018湖南邀請賽《2018》題解

6286 CCPC 2018湖南邀請賽《2018》題解

連結: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;
}