Submission #1176018

#TimeUsernameProblemLanguageResultExecution timeMemory
1176018quanndEvent Hopping (BOI22_events)C++20
0 / 100
48 ms25780 KiB
#include <bits/stdc++.h>
using namespace std;
 
#define int long long
#define pii pair<int,int>
 
// Số bước nhảy tối đa (với n <= 1e5, log2(1e5) < 20)
const int LIM = 20;
 
int n, q;
pii a[100005]; // Lưu các đoạn, sau sắp theo r tăng dần
// pos[ id ban đầu ] = vị trí của đoạn đó sau khi sắp (1-indexed)
int pos[100005];
 
// jump[i][k]: từ vị trí i trong mảng sắp, sau 2^k bước nhảy ngược, ta được vị trí nào.
int jump[100005][25];
 
// Hàm so sánh: sắp theo r tăng dần; nếu r bằng, sắp theo l tăng dần.
bool cmp(pii x, pii y){
    if(x.second == y.second) return x.first < y.first;
    return x.second < y.second;
}
 
// Ta xây dựng jump[i][0]:
// Từ vị trí i (i>=2) ta có thể nhảy về i-1 nếu thỏa điều kiện: l của a[i] <= r của a[i-1].
// Với i==1, không có ai phía trước.
 
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> n >> q;
    // Lưu các đoạn kèm id ban đầu (chỉ số từ 1 đến n)
    vector< pair<pii,int> > segs;
    for (int i = 1; i <= n; i++){
        int l, r;
        cin >> l >> r;
        segs.push_back({{l, r}, i});
    }
    
    // Sắp theo r tăng dần (nếu r bằng thì theo l)
    sort(segs.begin(), segs.end(), [&](auto A, auto B){
        if(A.first.second == B.first.second) return A.first.first < B.first.first;
        return A.first.second < B.first.second;
    });
    
    // Gán lại mảng a[] theo thứ tự sắp và lưu pos
    for (int i = 0; i < n; i++){
        a[i+1] = segs[i].first; // a[1..n]
        pos[segs[i].second] = i+1; // vị trí của đoạn có id ban đầu = segs[i].second
    }
    
    // Xây dựng jump[i][0]
    // Với i = 1: không có đoạn phía trước, nên jump[1][0] = 0.
    jump[1][0] = 0;
    for (int i = 2; i <= n; i++){
        // Kiểm tra điều kiện: từ a[i] (đích) thì có thể "nhảy ngược" từ i về i-1 nếu:
        // a[i].first (l của a[i]) <= a[i-1].second (r của a[i-1])
        if(a[i].first <= a[i-1].second)
            jump[i][0] = i - 1;
        else
            jump[i][0] = 0;
    }
    
    // Xây dựng bảng binary lifting
    for (int k = 1; k <= LIM; k++){
        for (int i = 1; i <= n; i++){
            int prev = jump[i][k-1];
            if(prev == 0) jump[i][k] = 0;
            else jump[i][k] = jump[prev][k-1];
        }
    }
    
    // Trả lời truy vấn:
    // Ý tưởng: cho truy vấn (x, y) theo chỉ số ban đầu.
    // Chuyển sang vị trí trong mảng sắp: u = pos[x], v = pos[y].
    // Vì chuỗi chuyển động của chúng ta là "nhảy ngược" (giảm chỉ số),
    // để có thể đi từ x đến y, ta cần u >= v.
    // Sau đó, dùng binary lifting để tìm số bước tối thiểu chuyển từ u về v.
    for (int i = 1; i <= q; i++){
        int X, Y;
        cin >> X >> Y;
        int u = pos[X], v = pos[Y];
        if(u < v){
            cout << "impossible" << "\n";
            continue;
        }
        // Nếu u == v, nghĩa là x và y là cùng một đoạn sau sắp.
        if(u == v){
            cout << 0 << "\n";
            continue;
        }
        
        int steps = 0;
        int cur = u;
        // Dùng binary lifting: cố gắng nhảy càng xa về phía đầu (giảm chỉ số)
        for (int k = LIM; k >= 0; k--){
            int nxtPos = jump[cur][k];
            // Nếu có thể nhảy và vẫn chưa vượt quá v (tức vẫn >= v)
            if(nxtPos != 0 && nxtPos >= v){
                cur = nxtPos;
                steps += (1LL << k);
            }
        }
        // Sau khi dùng binary lifting, ta cần kiểm tra xem đã đạt đúng vị trí v hay chưa.
        if(cur == v) cout << steps << "\n";
        else cout << "impossible" << "\n";
    }
    
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...