題解-APIO2019奇怪裝置

problem

loj-3144c++

題意概要:設函數 \(f(t)\) 的返回值爲一個二元組,即 \(f(t)=((t+\lfloor \frac tB\rfloor)\bmod A, t\bmod B)\),如今給出 \(n\) 個區間,問 \(t\) 在這 \(n\) 個區間中取值時,有多少個不一樣的 \(f(t)\)git

\(n\leq 10^6,\ l_i,r_i,A,B\leq 10^{18}\),區間互不相交函數

Solution

一開始沒啥想法,\(loj\) 的題面上寫了 \(l_i\leq r_i,r_i<l_i+1\)……這不就是說 \(l_i=r_i\) 嘛!暴力 \(O(n)\) 就行了!ui

其實是 \(r_i<l_{i+1}\)而後看着 \(5\) 分一檔的部分分陷入了沉思……後來直接想正解發現正解比暴力容易……spa

因爲不一樣的二元組難以考慮,考慮兩個二元組相同的狀況(即 \(f(t_1)=f(t_2)\))。同時這個二元組中的兩個函數中,第二維較爲簡單,考慮從這一維下手。code

因爲第二維要相同,因此兩個相同二元組必定是 \(f(x)\)\(f(x+kB)\) 形式的,再考慮第一維:
\[ x+\lfloor \frac xB\rfloor \equiv x+kB+\lfloor \frac {x+kB}B\rfloor \pmod A\\ x+\lfloor \frac xB\rfloor \equiv x+\lfloor \frac xB\rfloor +kB+k \pmod A\\ k(B+1)\equiv 0\pmod A \]
又因爲知足 \(k(B+1)\equiv 0\pmod A\) 的最小 \(k=\frac A{\gcd\{A,B+1\}}\)get

即知足 \(f(x)=f(y)\) 的,必定知足 \(\frac {AB}{\gcd \{A,B+1\}}|(y-x)\)。換種說法,也即 \(x\equiv y\pmod {\frac {AB}{\gcd\{A,B+1\}}}\)it

問題轉化爲在模 \(\frac {AB}{\gcd\{A,B+1\}}\) 意義下的覆蓋區間長度,時間複雜度 \(O(n\log n)\)io

Code

//loj-3144
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

template <typename _tp> inline void read(_tp&x){
    char ch=getchar();x=0;while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
}

inline ll gcd(ll A, ll B) {return B ? gcd(B, A%B) : A;}

const int N = 2001000;
typedef pair <ll,int> pli;
pli a[N];
int n, tot;
ll A, B;

int main() {
    read(n), read(A),read(B);
    ll d = A / gcd(A, B+1);
    ll l, r, l0, l1, r0, r1;
    bool flg = false;
    if(1e18 / B < d) {
        for(int i=1;i<=n;++i) {
            read(l), read(r);
            a[++tot] = pli(l, +1);
            a[++tot] = pli(r+1, -1);
        }
        flg = true;
    } else {
        d *= B;
        for(int i=1;i<=n;++i) {
            read(l), l0 = l % d, l1 = l / d;
            read(r), r0 = r % d, r1 = r / d;
            if(l1 == r1) {
                a[++tot] = pli(l0, +1);
                a[++tot] = pli(r0+1, -1);
            } else if(l1 + 1 == r1) {
                a[++tot] = pli(l0, +1);
                a[++tot] = pli(0, +1);
                a[++tot] = pli(r0+1, -1);
            } else return printf("%lld\n", d), 0;
        }
    }
    
    if(!flg) a[++tot] = pli(d, 0);
    a[0] = pli(0, 0);
    sort(a+1, a+tot+1);
    
    int vl = 0;
    ll Ans = 0ll;
    for(int i=1;i<=tot;++i) {
        if(vl) Ans += a[i].first - a[i-1].first;
        vl += a[i].second;
    }
    printf("%lld\n", Ans);
    return 0;
}
相關文章
相關標籤/搜索