Problem:
感染シミュレーション (Infection Simulation)
問題文
EGOI 食堂には昨日
N
人の客が来店した．
客には
1
から
N
までの番号が付けられており，客
i
(
1 ≦ i ≦ N
) の来店時刻は
L
i
，退店時刻は
R
i
であった．
そして今日，客のうち
1
人が，現在 JOI 国で流行している新型の感染症 X に感染した状態で来店したことが明らかになった．
感染症 X の
感染しづらさ
は整数
x
で表される．
具体的には，
1 ≦ i ≦ N
について，客
i
が
1
人以上の感染者と同時に食堂内にいた時間の累計が
x
以上となったタイミングで，客
i
は新たに感染者となる．
さて，JOI 国では厳格な感染症対策を行っているため，感染者数を正確に把握しなければならない．
しかし困ったことに，誰が感染症 X に感染したかの情報は得られておらず，感染しづらさを表す整数
x
も分かっていない．
そこで EGOI 食堂の店長である理恵さんは，
Q
個のシナリオについて，最終的に何人の客が感染するのかを求めることにした．
j
番目 (
1 ≦ j ≦ Q
) のシナリオでは，最初の感染者が客
P
j
のみであり，感染症 X の感染しづらさが
X
j
である．
来店した客およびシナリオの情報が与えられたとき，それぞれのシナリオにおける最終的な感染者数を出力するプログラムを作成せよ．
ただし，退店時刻ちょうどに感染した場合も，感染者数に含めるものとする．
また，感染症 X に一度感染した客が感染者でなくなることは考えないものとする．
制約
1 ≦ N ≦ 100 000
．
0 ≦ L
i
＜ R
i
≦ 10
9
(
1 ≦ i ≦ N
)．
1 ≦ Q ≦ 100 000
．
1 ≦ P
j
≦ N
(
1 ≦ j ≦ Q
)．
1 ≦ X
j
≦ 10
9
(
1 ≦ j ≦ Q
)．
入力される値はすべて整数である．
入力
入力は以下の形式で与えられる．
N
L
1
R
1
L
2
R
2
︙
L
N
R
N
Q
P
1
X
1
P
2
X
2
︙
P
Q
X
Q
出力
Q
行出力せよ．
j
行目 (
1 ≦ j ≦ Q
) には，
j
番目のシナリオにおける最終的な感染者数を出力せよ．
入出力例
入力例 1
4
10 40
20 80
45 60
70 95
1
1 15
出力例 1
3
1
番目のシナリオでは，最初の感染者が客
1
，感染症 X の感染しづらさが
15
であるため，以下のようにして感染が広がる．
時刻
10
に客
1
が来店する．
時刻
20
に客
2
が来店する．
時刻
35
に客
2
が
1
人以上の感染者と同時に食堂内にいた時間の累計が
15
となり，客
2
が感染する．
時刻
40
に客
1
が退店する．
時刻
45
に客
3
が来店する．
時刻
60
に客
3
が
1
人以上の感染者と同時に食堂内にいた時間の累計が
15
となり，客
3
が感染する．同時に，客
3
が退店する．
時刻
70
に客
4
が来店する．
時刻
80
に客
2
が退店する．
時刻
95
に客
4
が退店する．客
4
が
1
人以上の感染者と同時に食堂内にいたのは時刻
70
から時刻
80
までであり，時間の累計は
10
であるため，客
4
は感染しない．
最終的に客
1, 2, 3
の
3
人が感染するため，
1
行目には
3
を出力する．
入力例 2
8
0 30
0 90
0 80
0 60
0 20
0 40
0 70
0 50
3
1 30
1 40
4 50
出力例 2
7
1
5
1
番目のシナリオでは，最終的に客
1, 2, 3, 4, 6, 7, 8
の
7
人が感染するため，
1
行目には
7
を出力する．
2
番目のシナリオでは，最終的に客
1
の
1
人が感染するため，
2
行目には
1
を出力する．
3
番目のシナリオでは，最終的に客
2, 3, 4, 7, 8
の
5
人が感染するため，
3
行目には
5
を出力する．
入力例 3
5
0 10
0 10
0 10
0 10
0 10
4
1 9
1 10
1 11
1 1000000000
出力例 3
5
5
1
1
入力例 4
7
38 61
13 27
10 54
22 56
49 75
27 47
70 99
1
3 10
出力例 4
6
入力例 5
10
10 20
11 21
13 23
16 26
20 30
25 35
31 41
38 48
46 56
80 90
4
1 3
1 6
1 8
1 10
出力例 5
8
5
3
1
入力例 6
7
10 54
38 61
13 27
22 56
49 75
27 47
70 99
5
1 3
1 6
1 9
1 12
1 15
出力例 6
7
6
6
6
4
入力例 7
7
38 61
13 27
10 54
22 56
49 75
27 47
70 99
5
1 10
2 10
3 10
4 10
5 10
出力例 7
4
6
6
5
2
情報オリンピック日本委員会
作『日本情報オリンピック第4回女性部門 本選 競技課題』

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

int main(){
  int n; cin >> n; vector<int> B;
  vector<pair<int,int>> A(n); for (auto& [a,b]:A) (cin >> a >> b),B.emplace_back(b);
  sort(B.begin(),B.end()),B.erase(unique(B.begin(),B.end()),B.end()); int m = B.size();
  vector<int> segtree(2*m); vector<vector<int>> dsegtree(2*m);
  auto psolve = [&](){
    multiset<int> M;vector<vector<int>> D(m);
    for (int i(0);i < n;++i) D[lower_bound(B.begin(),B.end(),A[i].second)-B.begin()].emplace_back(A[i].first);
    for (int i(m-1);i > -1;--i){
      if (*M.begin()>=B[i]) M.clear();
      segtree[m+i] = (M.empty()?0:B[i]-(*M.begin()));
      for (int a:D[i]) M.emplace(a);
    }
    for (int i(m-1);i;--i) segtree[i] = min(segtree[i<<1],segtree[i<<1|1]);
    for (int i(0);i < n;++i) for (int k(lower_bound(B.begin(),B.end(),A[i].second)-B.begin()+m);k;k>>=1){
      dsegtree[k].emplace_back(A[i].second-A[i].first);
    }
    for (int i(0);i < m+m;++i) sort(dsegtree[i].begin(),dsegtree[i].end());
  };
  psolve(); int q; cin >> q;
  //for (int i(m);i < m+m;++i) cout << segtree[i] << endl;
  while(q--){
    int p,e; cin >> p >> e; --p; int bf = 0,bs = 0;
    for (int l(lower_bound(B.begin(),B.end(),A[p].second)-B.begin()+m),r(m+m);l < r;l>>=1,r>>=1){
      //cout << m << ' ' << l << ' ' << r << endl;
      if (l&1) if (segtree[l++]<e&&bf==0) bf = l-1;
      if (r&1) if (segtree[--r]<e) bs = r;
    }
    int i = (bf?bf:bs);
    while(i<m){
      i<<=1;
      if (segtree[i]>=e) i|=1;
    }
    int res(0);
    for (int l(lower_bound(B.begin(),B.end(),A[p].first+e)-B.begin()+m),r(i+1);l < r;l>>=1,r>>=1){
      //cout << l << ' ' << r << ' ';
      if (l&1) res += dsegtree[l].end()-lower_bound(dsegtree[l].begin(),dsegtree[l].end(),e),++l;
      if (r&1) --r,res += dsegtree[r].end()-lower_bound(dsegtree[r].begin(),dsegtree[r].end(),e);
      //cout << res << endl;
    }
    cout << (A[p].second-A[p].first<e?1:res) << endl;
  }
}
accuracy: 1
time_ms: 380
memory_kb: 32376
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;
const int MAX = 100007;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

int L[MAX], R[MAX];
int P[MAX], X[MAX];
int par[MAX], seg[MAX];
int ans[MAX];

struct UnionFind {
    int parent[MAX];
    UnionFind() {
        iota(parent, parent + MAX, 0);
    }
    int find(int x) {
        if (x == parent[x]) return x;
        return parent[x] = find(parent[x]);
    }
    void merge(int a, int b) {
        a = find(a), b = find(b);
        if (a == b) return;
        parent[a] = b;
    }
} uf;

struct FenwickTree {
    int tree[MAX] = {0};
    void update(int i, int v) {
        for (; i < MAX; i += i & -i) tree[i] += v;
    }
    int query(int L, int R) {
        int sum = 0;
        L--;
        for (; R; R -= R & -R) sum += tree[R];
        for (; L; L -= L & -L) sum -= tree[L];
        return sum;
    }
} fenw;

int main() {
    int N = Cin();
    for (int i = 1; i <= N; ++i) L[i] = Cin(), R[i] = Cin();
    int Q = Cin();
    for (int i = 1; i <= Q; ++i) P[i] = Cin(), X[i] = Cin();

    vector<pair<pair<int, int>, int>> ord;
    for (int i = 1; i <= N; ++i) ord.emplace_back(make_pair(L[i], R[i]), i);
    sort(ord.begin(), ord.end(), [&](auto a, auto b) {
        auto [leftA, rightA] = a.first;
        auto [leftB, rightB] = b.first;
        if (leftA != leftB) return leftA < leftB;
        if (rightA != rightB) return rightA > rightB;
        return a.second < b.second;
    });

    vector<pair<pair<int, int>, int>> segStack;
    vector<pair<int, int>> nestedSegments;
    for (int i = 0; i < N; ++i) {
        auto [currentLeftRight, segID] = ord[i];
        auto [curLeft, curRight] = currentLeftRight;
        if (!segStack.empty() && curRight <= segStack.back().first.second) {
            par[segID] = segStack.back().second;
            nestedSegments.push_back(currentLeftRight);
        }
        else {
            seg[segID] = segStack.size();
            par[segID] = segID;
            segStack.push_back({currentLeftRight, segID});
        }
    }

    vector<pair<int, int>> gaps;
    int stackSize = segStack.size();
    for (int i = 0; i + 1 < stackSize; ++i) {
        int gap = segStack[i].first.second - segStack[i + 1].first.first;
        gaps.emplace_back(gap, i);
    }
    sort(gaps.begin(), gaps.end());

    vector<tuple<int, int, int>> queries;
    for (int i = 1; i <= Q; ++i) {
        if (R[P[i]] - L[P[i]] < X[i]) {
            ans[i] = 1;
            continue;
        }
        queries.emplace_back(X[i], seg[par[P[i]]], i);
    }
    sort(queries.begin(), queries.end(), greater<tuple<int, int, int>>());

    vector<pair<int, int>> segLengthRight;
    vector<int> uniqueRight;
    uniqueRight.push_back(-1000000000);
    for (int i = 1; i <= N; ++i) {
        uniqueRight.push_back(R[i]);
        segLengthRight.emplace_back(R[i] - L[i], R[i]);
    }
    sort(uniqueRight.begin(), uniqueRight.end());
    uniqueRight.erase(unique(uniqueRight.begin(), uniqueRight.end()), uniqueRight.end());
    sort(segLengthRight.begin(), segLengthRight.end());

    for (auto [thresh, stackPos, qID] : queries) {
        while (!gaps.empty() && thresh <= gaps.back().first) {
            auto [_, pos] = gaps.back();
            gaps.pop_back();
            uf.merge(pos, pos + 1);
        }
        int rootIndex = uf.find(stackPos);
        int queryLeft = L[P[qID]];
        int queryRight = segStack[rootIndex].first.second;

        while (!segLengthRight.empty() && thresh <= segLengthRight.back().first) {
            auto [_, rightVal] = segLengthRight.back();
            segLengthRight.pop_back();
            int pos = lower_bound(uniqueRight.begin(), uniqueRight.end(), rightVal) - uniqueRight.begin();
            fenw.update(pos, 1);
        }
        int leftIndex = lower_bound(uniqueRight.begin(), uniqueRight.end(), queryLeft + thresh) - uniqueRight.begin();
        int rightIndex = upper_bound(uniqueRight.begin(), uniqueRight.end(), queryRight) - uniqueRight.begin();
        ans[qID] = fenw.query(leftIndex, rightIndex - 1);
    }
    for (int i = 1; i <= Q; ++i) Cout(ans[i]);
    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 14336
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
constexpr int MAX = 100007;

struct UnionFind {
    vector<int> parent;
    UnionFind(int n) : parent(n) { iota(parent.begin(), parent.end(), 0); }
    int find(int x) { return x == parent[x] ? x : parent[x] = find(parent[x]); }
    void merge(int a, int b) {
        a = find(a), b = find(b);
        if (a != b) parent[a] = b;
    }
};

struct FenwickTree {
    vector<int> tree;
    int N;
    FenwickTree(int n) : tree(n + 2, 0), N(n + 2) {}
    void update(int i, int v) {
        for (; i < N; i += i & -i) tree[i] += v;
    }
    int query(int L, int R) {
        int sum = 0;
        L--;
        for (; R; R -= R & -R) sum += tree[R];
        for (; L; L -= L & -L) sum -= tree[L];
        return sum;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N; cin >> N;
    vector<int> L(N + 1), R(N + 1);
    for (int i = 1; i <= N; ++i) cin >> L[i] >> R[i];
    int Q; cin >> Q;
    vector<int> P(Q + 1), X(Q + 1);
    for (int i = 1; i <= Q; ++i) cin >> P[i] >> X[i];

    vector<pair<pair<int, int>, int>> ord;
    for (int i = 1; i <= N; ++i)
        ord.emplace_back(make_pair(L[i], R[i]), i);

    sort(ord.begin(), ord.end(), [&](auto& a, auto& b) {
        auto [leftA, rightA] = a.first;
        auto [leftB, rightB] = b.first;
        if (leftA != leftB) return leftA < leftB;
        if (rightA != rightB) return rightA > rightB;
        return a.second < b.second;
    });

    vector<pair<pair<int, int>, int>> segStack;
    vector<pair<int, int>> nestedSegments;
    vector<int> par(N + 1), seg(N + 1);
    for (int i = 0; i < N; ++i) {
        auto [currentLeftRight, segID] = ord[i];
        auto [curLeft, curRight] = currentLeftRight;
        if (!segStack.empty() && curRight <= segStack.back().first.second) {
            par[segID] = segStack.back().second;
            nestedSegments.push_back(currentLeftRight);
        } else {
            seg[segID] = segStack.size();
            par[segID] = segID;
            segStack.emplace_back(currentLeftRight, segID);
        }
    }

    vector<pair<int, int>> gaps;
    int stackSize = segStack.size();
    for (int i = 0; i + 1 < stackSize; ++i) {
        int gap = segStack[i].first.second - segStack[i + 1].first.first;
        gaps.emplace_back(gap, i);
    }
    sort(gaps.begin(), gaps.end());

    vector<tuple<int, int, int>> queries;
    vector<int> ans(Q + 1, 0);
    for (int i = 1; i <= Q; ++i) {
        if (R[P[i]] - L[P[i]] < X[i]) {
            ans[i] = 1;
            continue;
        }
        queries.emplace_back(X[i], seg[par[P[i]]], i);
    }
    sort(queries.begin(), queries.end(), greater<tuple<int, int, int>>());

    vector<pair<int, int>> segLengthRight;
    vector<int> uniqueRight = {-1000000000};
    for (int i = 1; i <= N; ++i) {
        uniqueRight.push_back(R[i]);
        segLengthRight.emplace_back(R[i] - L[i], R[i]);
    }
    sort(uniqueRight.begin(), uniqueRight.end());
    uniqueRight.erase(unique(uniqueRight.begin(), uniqueRight.end()), uniqueRight.end());
    sort(segLengthRight.begin(), segLengthRight.end());

    UnionFind uf(stackSize + 2);
    FenwickTree fenw(uniqueRight.size() + 2);

    for (auto [thresh, stackPos, qID] : queries) {
        while (!gaps.empty() && thresh <= gaps.back().first) {
            auto [_, pos] = gaps.back();
            gaps.pop_back();
            uf.merge(pos, pos + 1);
        }
        int rootIndex = uf.find(stackPos);
        int queryLeft = segStack[rootIndex].first.first;
        int queryRight = segStack[rootIndex].first.second;

        while (!segLengthRight.empty() && thresh <= segLengthRight.back().first) {
            auto [_, rightVal] = segLengthRight.back();
            segLengthRight.pop_back();
            int pos = lower_bound(uniqueRight.begin(), uniqueRight.end(), rightVal) - uniqueRight.begin();
            fenw.update(pos, 1);
        }
        int leftIndex = lower_bound(uniqueRight.begin(), uniqueRight.end(), queryLeft + thresh) - uniqueRight.begin();
        int rightIndex = upper_bound(uniqueRight.begin(), uniqueRight.end(), queryRight) - uniqueRight.begin();
        ans[qID] = fenw.query(leftIndex, rightIndex - 1);
    }

    for (int i = 1; i <= Q; ++i) cout << ans[i] << '\n';
    return 0;
}
accuracy: 0.08620689655172414
time_ms: 40
memory_kb: 11020
final_rank: 6

code_snippet: // AOJ #790 Infection Simulation
// 2025.3.7
// C++20に適合させ、コメントを追加

#include <iostream> // std::cin, std::cout の代わりに getchar_unlocked, putchar_unlocked を使用
#include <vector>
#include <algorithm> // std::sort, std::unique, std::lower_bound, std::upper_bound
#include <numeric>   // std::iota
#include <utility>   // std::pair, std::make_pair
#include <tuple>     // std::tuple, std::get

// 定数
const int MAX_N_Q = 100007; // N および Q の最大値 +α
const int INF_NEG = -1000000000; // 非常に小さい値

// 高速入出力関数 (元のコードから流用)
// getchar_unlocked, putchar_unlocked は POSIX 環境でのみ利用可能で、
// 標準C++ではないため、環境によってはコンパイルエラーになる可能性があります。
// 標準的な std::cin, std::cout を使う場合は、この部分を置き換えてください。
#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() { // 整数の高速入力
    int n = 0;
    char c = gc();
    // 負の数には非対応と仮定 (問題の制約 L_i >= 0 より)
    while (c < '0' || c > '9') c = gc(); // 数字が来るまで読み飛ばし
    do {
        n = 10 * n + (c & 0xf); // c - '0' と同等
        c = gc();
    } while (c >= '0' && c <= '9'); // 数字である間処理を続ける
    return n;
}

void Cout(int n) { // 整数の高速表示（出力）
    char b[30]; // 十分なサイズのバッファ
    if (n == 0) { // 0 の場合
        pc('0');
    } else {
        if (n < 0) { // 負の数の場合
            pc('-');
            n = -n;
        }
        int i = 0;
        while (n > 0) {
            b[i++] = (n % 10) + '0';
            n /= 10;
        }
        while (i-- > 0) { // 逆順に出力
            pc(b[i]);
        }
    }
    pc('\n');
}

// グローバル変数 (問題の制約に合わせてサイズを設定)
int L[MAX_N_Q], R[MAX_N_Q]; // 各客の来店時刻 L と退店時刻 R
int P[MAX_N_Q], X[MAX_N_Q]; // 各クエリの初期感染者 P と感染しきい値 X
int par[MAX_N_Q];           // 区間の親子関係 (包含関係) を示す配列
int seg_group_id[MAX_N_Q];  // 各区間が属する"スタック上の"区間グループのID
int ans[MAX_N_Q];           // 各クエリの答えを格納する配列

// Union-Find (素集合データ構造)
struct UnionFind {
    int parent[MAX_N_Q];

    UnionFind() {
        // 各要素の親を自身として初期化
        std::iota(parent, parent + MAX_N_Q, 0);
    }

    // x の根を見つける (経路圧縮付き)
    int find(int x) {
        if (x == parent[x]) return x;
        return parent[x] = find(parent[x]);
    }

    // a と b の属する集合を併合する
    void merge(int a, int b) {
        a = find(a);
        b = find(b);
        if (a == b) return; // 同じ集合に属する場合は何もしない
        parent[a] = b;      // a の根を b の根に繋ぐ
    }
} uf;

// Fenwick Tree (Binary Indexed Tree - BIT)
struct FenwickTree {
    int tree[MAX_N_Q] = {0}; // 内部配列を0で初期化

    // 位置 i に値 v を加算する
    void update(int i, int v) {
        // 1-indexed で動作するため、i > 0 を想定
        for (; i < MAX_N_Q; i += i & -i) { // i に i の最下位ビットを加算して次のインデックスへ
            tree[i] += v;
        }
    }

    // 区間 [L, R] (1-indexed) の合計を問い合わせる
    int query(int qL, int qR) {
        if (qL > qR) return 0; // 無効な区間の場合は0
        int sum_R = 0;
        // 内部の tree は 1-indexed の累積和を保持していると考える
        // query(R) - query(L-1) を計算する
        for (; qR > 0; qR -= qR & -qR) {
            sum_R += tree[qR];
        }
        qL--; // L-1 の和を求めるためにデクリメント
        int sum_L_minus_1 = 0;
        for (; qL > 0; qL -= qL & -qL) {
            sum_L_minus_1 += tree[qL];
        }
        return sum_R - sum_L_minus_1;
    }
};
FenwickTree fenw; // Fenwick Tree のインスタンス


int main() {
    // 高速化のためのおまじない (getchar_unlocked を使う場合は不要なことが多い)
    // std::ios_base::sync_with_stdio(false);
    // std::cin.tie(nullptr);

    int N = Cin(); // 客の数
    for (int i = 1; i <= N; ++i) {
        L[i] = Cin();
        R[i] = Cin();
    }

    int Q = Cin(); // クエリの数
    for (int i = 1; i <= Q; ++i) {
        P[i] = Cin(); // 初期感染者 (1-indexed)
        X[i] = Cin(); // 感染しきい値
    }

    // 客の滞在区間をソートするための準備
    // pair<pair<int, int>, int> は {{L_i, R_i}, i} を表す
    std::vector<std::pair<std::pair<int, int>, int>> ord;
    ord.reserve(N); // 事前にメモリを確保
    for (int i = 1; i <= N; ++i) {
        ord.emplace_back(std::make_pair(L[i], R[i]), i);
    }

    // 区間のソート:
    // 1. L_i が小さい順
    // 2. L_i が同じなら R_i が大きい順 (包含関係を見つけやすくするため)
    // 3. L_i, R_i が同じなら元のIDが小さい順 (安定ソートのため)
    std::sort(ord.begin(), ord.end(), [](const auto& a, const auto& b) {
        // C++17 構造化束縛 (ラムダ内でも使える)
        auto [leftA, rightA] = a.first;
        auto [leftB, rightB] = b.first;
        if (leftA != leftB) return leftA < leftB;
        if (rightA != rightB) return rightA > rightB; // R が大きい方を前に
        return a.second < b.second;
    });

    // 区間の包含関係を処理し、"スタック"構造を構築する
    // segStack は、互いに包含関係にない区間（または最も外側の区間）を保持する
    std::vector<std::pair<std::pair<int, int>, int>> segStack;
    // nestedSegments は、何らかの区間に包含される区間を保持する（このコードでは直接使われていないように見えるが、元の意図を尊重）
    // std::vector<std::pair<int, int>> nestedSegments; // この変数は最終的なロジックで使われていないためコメントアウトも検討

    for (int i = 0; i < N; ++i) {
        auto [currentLeftRight, segID] = ord[i]; // 現在処理中の区間とそのID
        auto [curLeft, curRight] = currentLeftRight;

        // segStack の末尾の区間が現在の区間を包含するかどうか
        // ord のソート順 (L昇順、R降順) により、新しく処理する区間が
        // stack の末尾の区間に包含される場合、それは nested segment となる。
        // そうでない場合は、stack の末尾の区間は現在の区間と交差しないか、
        // 現在の区間が stack の末尾の区間より「右」にあることになる。
        // curRight <= segStack.back().first.second の条件は、現在の区間の右端が
        // スタックのトップの区間の右端の内側にあるか等しいか、つまり包含関係を示唆する。
        while (!segStack.empty() && curRight <= segStack.back().first.second) {
             // 現在の区間がスタックトップに包含される場合
            par[segID] = segStack.back().second; // 親区間を設定
            // nestedSegments.push_back(currentLeftRight); // 元のコードにあったが、この変数は後続で使われていない
            // このループは、現在の区間を直接包含する最も内側のスタック上の区間を見つけるためのものかもしれない
            // ただし、元のコードのロジックでは、このループは1回で終わるか、入らない。
            // なぜなら、ソート順により L_current >= L_stack_top であり、
            // R_current <= R_stack_top ならば、ネストしている。
            // もし R_current > R_stack_top ならば、ネストしていないのでループに入らない。
            // もし L_current == L_stack_top ならば、R_current <= R_stack_top (ソート順より)
            // このループは、実際にはスタックトップが現在の区間を包含する場合のみ１回実行される
            // もしくは、より深い包含関係を探る意図があったのかもしれないが、現在のコードでは
            // スタックトップの区間が現在の区間を包含する場合のみ par が設定される。
            // 下の else if でスタックからpopするロジックがないため、
            // 厳密な「区間木」のようなスタック処理とは異なる。
             break; // 最初の包含親が見つかればOK
        }
        // 上のwhileは、実際にはifに近い挙動になるはず。
        // 元のコードのロजिक: if (!segStack.empty() && curRight <= segStack.back().first.second)
        if (!segStack.empty() && curRight <= segStack.back().first.second) {
             par[segID] = segStack.back().second;
        } else {
            // 現在の区間がスタックトップに包含されない場合、新しい"外側"の区間として扱う
            // seg_group_id は、この区間が segStack の何番目の要素に対応するかを示す
            seg_group_id[segID] = segStack.size();
            par[segID] = segID; // 自分自身を親とする (根)
            segStack.push_back({currentLeftRight, segID});
        }
    }


    // segStack に残った区間間の "ギャップ" を計算
    // ギャップ: segStack[i] の右端と segStack[i+1] の左端の差
    // これが小さいほど、これらの区間グループは「繋がりやすい」
    std::vector<std::pair<int, int>> gaps;
    int stackSize = segStack.size();
    if (stackSize > 0) { // stackSize が 0 の場合を考慮
        gaps.reserve(stackSize -1 > 0 ? stackSize -1 : 0);
    }


    for (int i = 0; i + 1 < stackSize; ++i) {
        // ギャップ = 前の区間の右端 - 次の区間の左端
        // このギャップは、感染が広がる際の「途切れ目」のコストと解釈できる
        int gap_value = segStack[i].first.second - segStack[i + 1].first.first;
        gaps.emplace_back(gap_value, i); // {ギャップの大きさ, segStack でのインデックス i}
    }
    std::sort(gaps.begin(), gaps.end()); // ギャップが小さい順にソート

    // クエリを処理しやすいように変換・ソート
    // tuple<int, int, int> は {X_j, seg_group_id[par[P_j]], 元のクエリID j}
    std::vector<std::tuple<int, int, int>> queries;
    queries.reserve(Q);
    for (int i = 1; i <= Q; ++i) {
        // 初期感染者自身の滞在期間が X_j より短い場合、感染は広がらず、感染者は1人
        if (R[P[i]] - L[P[i]] < X[i]) {
            ans[i] = 1;
            continue;
        }
        // P[i] の親 (最も外側の包含区間、または自身) が属する segStack 上のグループIDを取得
        // queries に {感染しきい値 X, 初期感染者の属するスタックグループID, 元のクエリID} を格納
        queries.emplace_back(X[i], seg_group_id[par[P[i]]], i);
    }
    // クエリを X_j (感染しきい値) が大きい順にソート
    // これにより、X_j が大きい (感染しにくい) ものから処理できる
    // ギャップをマージしていく際、X_j が大きいものは少ないマージで済む
    std::sort(queries.begin(), queries.end(), std::greater<std::tuple<int, int, int>>());


    // Fenwick Tree で使うための準備
    // segLengthRight: {区間の長さ, 区間の右端 R_i}
    // uniqueRight: 全ての R_i を集めてソートし、重複を除いたもの (座標圧縮用)
    std::vector<std::pair<int, int>> segLengthRight;
    std::vector<int> uniqueRight;
    segLengthRight.reserve(N);
    uniqueRight.reserve(N + 1); // ダミーの最小値も入れるため

    uniqueRight.push_back(INF_NEG); // 番兵として非常に小さい値を追加
    for (int i = 1; i <= N; ++i) {
        uniqueRight.push_back(R[i]);
        segLengthRight.emplace_back(R[i] - L[i], R[i]);
    }
    std::sort(uniqueRight.begin(), uniqueRight.end());
    // uniqueRight から重複を削除
    uniqueRight.erase(std::unique(uniqueRight.begin(), uniqueRight.end()), uniqueRight.end());
    // segLengthRight を区間の長さが大きい順にソート
    std::sort(segLengthRight.begin(), segLengthRight.end(), std::greater<std::pair<int, int>>());


    // ソートされたクエリを処理
    for (const auto& query_tuple : queries) {
        auto [thresh_X, initial_stack_pos, qID] = query_tuple; // 構造化束縛で要素を取得

        // 現在の感染しきい値 thresh_X 以下のギャップを持つ区間グループを Union-Find で併合
        // thresh_X が大きいものから処理しているので、gaps は後ろから見ていく
        // (gaps は小さい順にソートされているため)
        // thresh_X が小さくなるにつれて、より多くのギャップがマージ条件を満たす
        // 元のコードは gaps.back().first >= thresh_X だったが、
        // gaps は昇順ソート、queries は thresh_X の降順ソート。
        // よって、thresh_X が小さくなるにつれて、gaps の小さい方から条件を満たすようになる
        // しかし、元のコードは gaps を pop_back しているので、thresh_X が大きいギャップを先に処理する
        // gaps は昇順ソートなので、gaps.back() は最大のギャップ。
        // queries は X が大きい順。
        // X が大きい => 感染しにくい => ギャップが小さくないと繋がらない。
        // このループは、thresh_X が許容する「繋がりやすさ」に基づいて区間グループをマージする。
        // ギャップ <= X という条件なら、gaps の小さい方から見ていく。
        // 元のコードの `thresh <= gaps.back().first` (X_j <= gap_value) は、
        // X_j がこのギャップ値以下であるなら、このギャップでは連結しない、という意味に取れる。
        // しかし、UF.merge は連結させている。
        // おそらく意図は「現在の X で連結できるギャップは全て連結する」
        // X が大きいほど、連結できるギャップは少ない (大きなギャップは連結できない)
        // X が小さいほど、連結できるギャップは多い (小さなギャップは連結できる)
        // queries は X が大きい順。gaps は gap が小さい順。
        // X が大きいクエリでは、uf.merge する回数が少ない。
        // X が小さくなるにつれて、uf.merge が進む。
        // `thresh <= gaps.back().first` は、`X_j` が `gap` 以下の時。
        // X が大きい (例: 100) -> gap (例: 10) -> 100 <= 10 (false)
        // X が小さい (例: 5)  -> gap (例: 10) -> 5 <= 10 (true)
        // この条件だと、Xが小さいほどマージが進む。これは意図通り。
        // ただし、gaps.back() を使うので、Xが小さい時に「大きいギャップ」がマージされる。
        // 意図としては、X以上の接続コストを持つギャップは接続できない。
        // よって、Xより「小さい」ギャップを持つものをマージすべき。
        // `gaps.back().first < thresh_X` (現在のXより小さいギャップはマージ)
        // または `gaps.back().first <= thresh_X` (X以下のギャップはマージ)

        // 元コードのロジック `thresh <= gaps.back().first` は、
        // `X_j <= gap_value` を意味する。これは「感染しきい値がギャップ以下なら連結」
        // X_j が小さいほど感染しやすい。ギャップが小さいほど連結しやすい。
        // X_j が小さいほど、より多くのギャップが X_j より大きくなり、この条件は満たしにくくなる。
        // これは逆。X_j が「大きい」ほど、より多くの「小さい」ギャップを許容して連結するはず。
        // 正しい条件は `gaps.back().first < X_j` や `gaps.back().first <= X_j` であるべき。
        // ここでは元のコードのロジックを尊重しつつコメント。
        // (ソート順と `back()` の使い方から、`gaps.back().first` は現在処理可能な最大のギャップ)
        // queries が X の降順、gaps が gap の昇順。
        // X が大きいクエリから処理。この時、小さい gap から順にマージしていく。
        // そのためには、gaps のイテレータを保持する必要がある。
        // 以下は元のコードのロジックを再現
        while (!gaps.empty() && thresh_X <= gaps.back().first) { // X が gap以下のとき (Xが小さいほど条件を満たしやすい)
            // しかし、これは「Xが小さいほど大きなギャップをマージする」ことになり、直感と逆。
            // 正しくは「Xが大きいほど、より多くの(より小さい)ギャップをマージできる」
            // または「X以下の(小さい)ギャップだけをマージする」
            // ここでは、X_j 以上のギャップを持つものは連結しない、と解釈し、
            // X_j 未満のギャップを連結していく。
            // X_j の降順でクエリを処理するので、X_j が小さくなるにつれて、
            // それまで連結できなかったギャップが連結できるようになる、ということはない。
            // X_j が小さくなる => 連結の条件が厳しくなる。
            // よって、X_j が大きいときに連結したものは、X_j が小さくても連結したまま。
            // この部分は「X_j より小さいギャップを処理済みとする」オフライン処理と解釈する。
            // 現在の `thresh_X` よりも**小さいか等しい**ギャップを持つものをマージする。
            // `gaps`は昇順ソートされているので、先頭から見ていく。
            // `queries`は`thresh_X`の降順なので、`thresh_X`は小さくなっていく。
            // したがって、`gaps`の処理は一方向に進むポインタで管理できる。
            // --- ここから元のコードのロジックと異なる可能性のある解釈 ---
            // `thresh_X` (現在の感染のしにくさ)
            // `gaps.back().first` (スタックに残っている最大のギャップ)
            // もし `thresh_X <= gaps.back().first` ならば、
            // 感染のしにくさが、この最大のギャップよりも小さいか等しい。
            // これは、このギャップを乗り越えるのが難しい（またはギリギリ）という意味。
            // UFでマージするということは、このギャップを乗り越えて感染が広がることを意味する。
            // この条件は、X が小さい（感染しやすい）ほど、大きなギャップも許容して繋がる、という意味になる。
            // Xの降順ソートと合わせて考えると、Xが大きいときは一部のギャップしかマージされず、
            // Xが小さくなるにつれて、より多くの（より大きい）ギャップもマージされる。
            // これは直感的には正しい。
            // ただし、gapsは昇順ソート。gaps.back()は最大のギャップ。
            // このループはおそらく、現在のthresh_Xで連結「できない」ギャップをpopし、
            // 連結「できる」ギャップだけが残るようにする、という意図かもしれないが、
            // uf.mergeしているので、連結「できる」ものを処理している。
            // `thresh <= gaps.back().first` (X <= gap) -> Xが小さいほど真になりやすい
            //  -> Xが小さいほど、大きなgapもマージする
            // これは正しい。

            // ここで使うべき `gaps` のイテレータ
            static int gap_ptr = 0; // クエリ間で状態を保持
            if (qID == std::get<2>(queries[0])) gap_ptr = 0; // 最初のクエリでリセット

            // X_j 以上のギャップは連結できない。
            // X_j 未満のギャップは連結できる。
            // queries は X_j の降順。gaps は gap_value の昇順。
            // X_j が小さくなるにつれて、連結できるギャナップは減る。
            // これは、X_j が大きいときに連結したものは、小さい X_j でも連結したままというオフライン処理。
            // 正しいループ:
            // while(gap_ptr < gaps.size() && gaps[gap_ptr].first < thresh_X) {
            //    uf.merge(gaps[gap_ptr].second, gaps[gap_ptr].second + 1);
            //    gap_ptr++;
            // }
            // 元のコードのロジック (thresh <= gaps.back().first) で進める
            while (!gaps.empty() && thresh_X <= gaps.back().first) {
                 auto [_, pos] = gaps.back(); // _ はギャップ値 (使わない)
                 gaps.pop_back(); //処理したギャップは取り除く
                 uf.merge(pos, pos + 1); // 隣接するスタック区間グループを併合
            }

        }
        // 初期感染者の属するスタック区間グループの現在の根 (代表元) を求める
        int rootIndex = uf.find(initial_stack_pos);
        // 感染が広がる可能性のある左端 (初期感染者のL_i)
        int queryEffectiveLeft = L[P[qID]];
        // 感染が広がる可能性のある右端 (併合された区間グループ全体の右端)
        // rootIndex は segStack でのインデックス
        int queryEffectiveRight = segStack[rootIndex].first.second;


        // Fenwick Tree を使って、条件を満たす区間の数を数える
        // 条件: 区間長 >= thresh_X であり、
        //       区間の右端 R_k が [queryEffectiveLeft + thresh_X, queryEffectiveRight] に収まるもの。
        // segLengthRight は区間長が大きい順にソート済み
        // thresh_X はクエリ間で単調非増加 (大きい順に処理)
        // このループもオフライン処理。現在の thresh_X 以上の長さを持つ区間を Fenwick Tree に追加
        static int seg_len_ptr = 0; // クエリ間で状態を保持
        if (qID == std::get<2>(queries[0])) seg_len_ptr = 0; // 最初のクエリでリセット

        while(seg_len_ptr < segLengthRight.size() && thresh_X <= segLengthRight[seg_len_ptr].first) {
            auto [len, rightVal] = segLengthRight[seg_len_ptr]; // len は使わない
            // uniqueRight で rightVal の位置 (座標圧縮後のインデックス) を見つける
            // Fenwick Tree は 1-indexed なので +1 することが多いが、
            // lower_bound の結果は既に適切 (0-indexed 配列のインデックス)
            // 元のコードは lower_bound の結果をそのまま使っているので、それに従う。
            // uniqueRight[0] は番兵なので、結果は実質1-indexedのようになる。
            int pos_in_unique_rights = std::lower_bound(uniqueRight.begin(), uniqueRight.end(), rightVal) - uniqueRight.begin();
            fenw.update(pos_in_unique_rights, 1); // その位置に区間が1つ存在することを示す
            seg_len_ptr++;
        }

        // Fenwick Tree でクエリ範囲内の区間数を数える
        // queryLeft + thresh は、感染に必要な期間を考慮した実質的な左端
        // R_k >= queryLeft + thresh_X を満たす必要がある
        int fenwick_query_L_idx = std::lower_bound(uniqueRight.begin(), uniqueRight.end(), queryEffectiveLeft + thresh_X) - uniqueRight.begin();
        // R_k <= queryEffectiveRight を満たす必要がある
        int fenwick_query_R_idx = std::upper_bound(uniqueRight.begin(), uniqueRight.end(), queryEffectiveRight) - uniqueRight.begin() -1; // upper_bound は一つ後ろを指すので -1

        ans[qID] = fenw.query(fenwick_query_L_idx, fenwick_query_R_idx);
    }

    // 答えを出力
    for (int i = 1; i <= Q; ++i) {
        Cout(ans[i]);
    }

    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 13596
final_rank: 1

code_snippet: // AOJ #790 Infection Simulation
// 2025.3.7

#include <bits/stdc++.h>
using namespace std;
const int MAX = 100007;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

int L[MAX], R[MAX];
int P[MAX], X[MAX];
int par[MAX], seg[MAX];
int ans[MAX];

struct UnionFind {
    int parent[MAX];
    UnionFind() {
        iota(parent, parent + MAX, 0);
    }
    int find(int x) {
        if (x == parent[x]) return x;
        return parent[x] = find(parent[x]);
    }
    void merge(int a, int b) {
        a = find(a), b = find(b);
        if (a == b) return;
        parent[a] = b;
    }
} uf;

struct FenwickTree {
    int tree[MAX] = {0};
    void update(int i, int v) {
        for (; i < MAX; i += i & -i) tree[i] += v;
    }
    int query(int L, int R) {
        int sum = 0;
        L--;
        for (; R; R -= R & -R) sum += tree[R];
        for (; L; L -= L & -L) sum -= tree[L];
        return sum;
    }
} fenw;

int main() {
    int N = Cin();
    for (int i = 1; i <= N; ++i) L[i] = Cin(), R[i] = Cin();
    int Q = Cin();
    for (int i = 1; i <= Q; ++i) P[i] = Cin(), X[i] = Cin();

    vector<pair<pair<int, int>, int>> ord;
    for (int i = 1; i <= N; ++i) ord.emplace_back(make_pair(L[i], R[i]), i);
    sort(ord.begin(), ord.end(), [&](auto a, auto b) {
        auto [leftA, rightA] = a.first;
        auto [leftB, rightB] = b.first;
        if (leftA != leftB) return leftA < leftB;
        if (rightA != rightB) return rightA > rightB;
        return a.second < b.second;
    });

    vector<pair<pair<int, int>, int>> segStack;
    vector<pair<int, int>> nestedSegments;
    for (int i = 0; i < N; ++i) {
        auto [currentLeftRight, segID] = ord[i];
        auto [curLeft, curRight] = currentLeftRight;
        if (!segStack.empty() && curRight <= segStack.back().first.second) {
            par[segID] = segStack.back().second;
            nestedSegments.push_back(currentLeftRight);
        }
        else {
            seg[segID] = segStack.size();
            par[segID] = segID;
            segStack.push_back({currentLeftRight, segID});
        }
    }

    vector<pair<int, int>> gaps;
    int stackSize = segStack.size();
    for (int i = 0; i + 1 < stackSize; ++i) {
        int gap = segStack[i].first.second - segStack[i + 1].first.first;
        gaps.emplace_back(gap, i);
    }
    sort(gaps.begin(), gaps.end());

    vector<tuple<int, int, int>> queries;
    for (int i = 1; i <= Q; ++i) {
        if (R[P[i]] - L[P[i]] < X[i]) {
            ans[i] = 1;
            continue;
        }
        queries.emplace_back(X[i], seg[par[P[i]]], i);
    }
    sort(queries.begin(), queries.end(), greater<tuple<int, int, int>>());

    vector<pair<int, int>> segLengthRight;
    vector<int> uniqueRight;
    uniqueRight.push_back(-1000000000);
    for (int i = 1; i <= N; ++i) {
        uniqueRight.push_back(R[i]);
        segLengthRight.emplace_back(R[i] - L[i], R[i]);
    }
    sort(uniqueRight.begin(), uniqueRight.end());
    uniqueRight.erase(unique(uniqueRight.begin(), uniqueRight.end()), uniqueRight.end());
    sort(segLengthRight.begin(), segLengthRight.end());

    for (auto [thresh, stackPos, qID] : queries) {
        while (!gaps.empty() && thresh <= gaps.back().first) {
            auto [_, pos] = gaps.back();
            gaps.pop_back();
            uf.merge(pos, pos + 1);
        }
        int rootIndex = uf.find(stackPos);
        int queryLeft = L[P[qID]];
        int queryRight = segStack[rootIndex].first.second;

        while (!segLengthRight.empty() && thresh <= segLengthRight.back().first) {
            auto [_, rightVal] = segLengthRight.back();
            segLengthRight.pop_back();
            int pos = lower_bound(uniqueRight.begin(), uniqueRight.end(), rightVal) - uniqueRight.begin();
            fenw.update(pos, 1);
        }
        int leftIndex = lower_bound(uniqueRight.begin(), uniqueRight.end(), queryLeft + thresh) - uniqueRight.begin();
        int rightIndex = upper_bound(uniqueRight.begin(), uniqueRight.end(), queryRight) - uniqueRight.begin();
        ans[qID] = fenw.query(leftIndex, rightIndex - 1);
    }
    for (int i = 1; i <= Q; ++i) Cout(ans[i]);
    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 14416
final_rank: 3

code_snippet: // AOJ #790
// Infection Simulation 2025.2.9

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0, c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(ll n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

struct Interval { ll L, R; ll diff; };
 
int N;
vector<Interval> intervals;
vector<Interval> sortedByL;
vector<ll> sortedL;
vector<ll> prefixMaxR;
 
void buildSortedByL(){
    sortedByL = intervals;
    sort(sortedByL.begin(), sortedByL.end(), [](const Interval &a, const Interval &b){
        return a.L < b.L;
    });
    int n = sortedByL.size();
    sortedL.resize(n);
    prefixMaxR.resize(n);
    for (int i = 0; i < n; i++){
        sortedL[i] = sortedByL[i].L;
        if(i==0) prefixMaxR[i] = sortedByL[i].R;
        else prefixMaxR[i] = max(prefixMaxR[i-1], sortedByL[i].R);
    }
}
 
ll F_func(ll t){
    int pos = upper_bound(sortedL.begin(), sortedL.end(), t) - sortedL.begin() - 1;
    if(pos < 0) return t;
    return prefixMaxR[pos];
}
 
struct SegTree {
    int n;
    vector<vector<ll>> treeR;
    SegTree(int n) : n(n) {
        treeR.resize(4*n);
    }
    void build(int idx, int l, int r) {
        if(l==r)
            treeR[idx].push_back(sortedByL[l].R);
        else {
            int mid = (l+r)/2;
            build(idx*2, l, mid);
            build(idx*2+1, mid+1, r);
            treeR[idx].resize(treeR[idx*2].size() + treeR[idx*2+1].size());
            merge(treeR[idx*2].begin(), treeR[idx*2].end(),
                  treeR[idx*2+1].begin(), treeR[idx*2+1].end(),
                  treeR[idx].begin());
        }
    }
    void init() { build(1, 0, n-1); }
    int queryR_ge(int idx, int l, int r, int ql, int qr, ll X) {
        if(ql > r || qr < l) return 0;
        if(ql<=l && r<=qr){
            auto &vec = treeR[idx];
            int pos = lower_bound(vec.begin(), vec.end(), X) - vec.begin();
            return vec.size() - pos;
        }
        int mid = (l+r)/2;
        return queryR_ge(idx*2, l, mid, ql, qr, X) + queryR_ge(idx*2+1, mid+1, r, ql, qr, X);
    }
};
 
SegTree* segTreePtr = nullptr;
 
struct MergeTree {
    int n;
    vector<vector<pair<ll,ll>>> tree;
    MergeTree(int n): n(n) { tree.resize(4*n); }
    void build(int idx, int l, int r) {
        if(l == r)
            tree[idx].push_back({ sortedByL[l].R, sortedByL[l].R - sortedByL[l].L });
        else {
            int mid = (l+r)/2;
            build(idx*2, l, mid);
            build(idx*2+1, mid+1, r);
            int sz1 = tree[idx*2].size(), sz2 = tree[idx*2+1].size();
            tree[idx].resize(sz1+sz2);
            merge(tree[idx*2].begin(), tree[idx*2].end(),
                  tree[idx*2+1].begin(), tree[idx*2+1].end(),
                  tree[idx].begin(), [](auto &a, auto &b){
                      return a.second < b.second;
                  });
        }
    }
    int query(int idx, int l, int r, int ql, int qr, ll A, ll x) {
        if(ql > r || qr < l) return 0;
        if(ql<=l && r<=qr){
            auto &vec = tree[idx];
            int pos = lower_bound(vec.begin(), vec.end(), make_pair(-LLONG_MAX, x),
                                  [](const pair<ll,ll>& a, const pair<ll,ll>& b){
                                      return a.second < b.second;
                                  }) - vec.begin();
            int cnt = 0;
            for(int i = pos; i < vec.size(); i++){
                if(vec[i].first <= A) cnt++;
            }
            return cnt;
        }
        int mid = (l+r)/2;
        return query(idx*2, l, mid, ql, qr, A, x) + query(idx*2+1, mid+1, r, ql, qr, A, x);
    }
};
 
MergeTree* mergeTreePtr = nullptr;
 
int main() {
    N = Cin();
    intervals.resize(N);
    for (int i = 0; i < N; i++){
        ll L = Cin(), R = Cin(); 
        intervals[i].L = L; 
        intervals[i].R = R;
        intervals[i].diff = R - L;
    }
    buildSortedByL();
    int nSorted = sortedByL.size();
 
    segTreePtr = new SegTree(nSorted);
    segTreePtr->init();
 
    mergeTreePtr = new MergeTree(nSorted);
    mergeTreePtr->build(1, 0, nSorted-1);
 
    int Q = Cin();
    for (int qi = 0; qi < Q; qi++){
        int p = Cin()-1; ll x = Cin();
 
        ll L0 = intervals[p].L;
        ll A = intervals[p].R;
 
        int iterCount = 0;
        while (A - L0 >= x) {
            ll arg = A - x;
            if(arg < L0) arg = L0;
            ll cand = F_func(arg);
            if(cand > A) { A = cand; }
            else break;
            iterCount++;
            if(iterCount > 1000000) break;
        }
        ll finalA = A;
        int infectedCount = 0;
 
        int posGroup1 = lower_bound(sortedL.begin(), sortedL.end(), L0) - sortedL.begin();
        int group1Count = 0;
        if(posGroup1 > 0)
            group1Count = segTreePtr->queryR_ge(1, 0, nSorted-1, 0, posGroup1-1, L0 + x);
 
        int posL = lower_bound(sortedL.begin(), sortedL.end(), L0) - sortedL.begin();
        int posR = upper_bound(sortedL.begin(), sortedL.end(), finalA - x) - sortedL.begin();
 
        int group2Count = 0;
        if(posL < posR){
            int countB = segTreePtr->queryR_ge(1, 0, nSorted-1, posL, posR-1, finalA+1);
            int countA = mergeTreePtr->query(1, 0, nSorted-1, posL, posR-1, finalA, x);
            group2Count = countA + countB;
        }
 
        infectedCount = group1Count + group2Count;
        ll expP = min(intervals[p].R, finalA) - L0;
        bool countedP = (expP >= x);
        if(!countedP) infectedCount++;
        Cout(infectedCount);
    }
    return 0;
}
accuracy: 0.13793103448275862
time_ms: 1940
memory_kb: 76208
final_rank: 5

code_snippet: // AOJ #790
// Infection Simulation 2025.2.9

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(ll n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

struct Interval { ll L, R; ll diff; };
 
int N;
vector<Interval> intervals;
vector<Interval> sortedByL;
vector<ll> sortedL;
vector<ll> prefixMaxR;
 
void buildSortedByL(){
    sortedByL = intervals;
    sort(sortedByL.begin(), sortedByL.end(), [](const Interval &a, const Interval &b){
        return a.L < b.L;
    });
    int sz = sortedByL.size();
    sortedL.resize(sz);
    prefixMaxR.resize(sz);
    for (int i = 0; i < sz; i++){
        sortedL[i] = sortedByL[i].L;
        if(i == 0) prefixMaxR[i] = sortedByL[i].R;
        else prefixMaxR[i] = max(prefixMaxR[i-1], sortedByL[i].R);
    }
}
 
ll F_func(ll t) {
    int pos = upper_bound(sortedL.begin(), sortedL.end(), t) - sortedL.begin() - 1;
    if (pos < 0) return t;
    return prefixMaxR[pos];
}
 
struct SegTree {
    int n;
    vector<vector<ll>> segR;
    vector<vector<ll>> segDiff;
    SegTree(int n) : n(n) {
        segR.resize(4*n);
        segDiff.resize(4*n);
    }
    void build(vector<Interval>& arr, int idx, int l, int r) {
        if(l == r) {
            segR[idx].push_back(arr[l].R);
            segDiff[idx].push_back(arr[l].diff);
        } else {
            int mid = (l+r)/2;
            build(arr, idx*2, l, mid);
            build(arr, idx*2+1, mid+1, r);
            segR[idx].resize(segR[idx*2].size() + segR[idx*2+1].size());
            merge(segR[idx*2].begin(), segR[idx*2].end(),
                  segR[idx*2+1].begin(), segR[idx*2+1].end(),
                  segR[idx].begin());
 
            segDiff[idx].resize(segDiff[idx*2].size() + segDiff[idx*2+1].size());
            merge(segDiff[idx*2].begin(), segDiff[idx*2].end(),
                  segDiff[idx*2+1].begin(), segDiff[idx*2+1].end(),
                  segDiff[idx].begin());
        }
    }
    void init(vector<Interval>& arr) {
        build(arr, 1, 0, n-1);
    }
    int queryR_ge(int idx, int l, int r, int ql, int qr, ll X) {
        if(ql > r || qr < l) return 0;
        if(ql<=l && r<=qr) {
            return segR[idx].size() - (lower_bound(segR[idx].begin(), segR[idx].end(), X) - segR[idx].begin());
        }
        int mid = (l+r)/2;
        return queryR_ge(idx*2, l, mid, ql, qr, X) + queryR_ge(idx*2+1, mid+1, r, ql, qr, X);
    }
    int queryR_le(int idx, int l, int r, int ql, int qr, ll X) {
        if(ql > r || qr < l) return 0;
        if(ql<=l && r<=qr) {
            return upper_bound(segR[idx].begin(), segR[idx].end(), X) - segR[idx].begin();
        }
        int mid = (l+r)/2;
        return queryR_le(idx*2, l, mid, ql, qr, X) + queryR_le(idx*2+1, mid+1, r, ql, qr, X);
    }
};
 
SegTree* segTreePtr = nullptr;
 
int main(){
    N = Cin();
    intervals.resize(N);
    for (int i = 0; i < N; i++){
        ll L = Cin(), R = Cin();
        intervals[i].L = L;
        intervals[i].R = R;
        intervals[i].diff = R - L;
    }
    buildSortedByL();
    int nSorted = sortedByL.size();
    segTreePtr = new SegTree(nSorted);
    segTreePtr->init(sortedByL);
 
    int Q = Cin();
    for (int qi = 0; qi < Q; qi++){
        int p = Cin()-1; ll x = Cin();
 
        ll L0 = intervals[p].L;
        ll A = intervals[p].R;
 
        int iterCount = 0;
        while (A - L0 >= x) {
            ll arg = A - x;
            if(arg < L0) arg = L0;
            ll cand = F_func(arg);
            if(cand > A) {
                A = cand;
            } else break;
            iterCount++;
            if(iterCount > 200000) break;
        }
        ll finalA = A;
 
 
        int infectedCount = 0;
        int idx = lower_bound(sortedL.begin(), sortedL.end(), L0) - sortedL.begin();
        int group1Count = 0;
        if(idx > 0)
            group1Count = segTreePtr->queryR_ge(1, 0, nSorted-1, 0, idx-1, L0 + x);
 
        int group2Count = 0;
        for (int i = idx; i < nSorted; i++){
            if(sortedByL[i].R <= finalA)
                if(sortedByL[i].R - sortedByL[i].L >= x) group2Count++;
            else {
                if(sortedByL[i].L <= finalA - x) group2Count++;
            }
        }
 
        infectedCount = group1Count + group2Count;
 
        ll expP = min(intervals[p].R, finalA) - L0;
        bool countedP = (expP >= x);
        if(!countedP) infectedCount++;
        Cout(infectedCount);
    }
    return 0;
}
accuracy: 0.034482758620689655
time_ms: 20
memory_kb: 62840
final_rank: 7