公式

E - 信号変換器の出力種類数 / Number of Distinct Outputs of a Signal Converter 解説 by admin

Claude 4.6 Opus (Thinking)

Overview

Given an \(N\)-step signal converter, we need to compute the output \(F(s)\) for each input \(s\), and then efficiently count the number of distinct values of \(F(s)\) within the interval \([A_j, B_j]\) for each query.

Analysis

Step 1: Computing \(F(s)\)

First, we need to compute the output \(F(s)\) for all input values \(s\) (\(1 \leq s \leq K\)). By straightforwardly simulating the \(N\) steps for each \(s\), we can compute this in \(O(NK)\). Since the constraints guarantee \(N \times K \leq 10^7\), this is sufficiently fast.

For example, with \(K=5\), \(N=2\), and steps \((2, 4, 3)\) and \((3, 5, 1)\):

\(s\) After Step 1 After Step 2 = \(F(s)\)
1 1 1
2 3 1
3 3 1
4 3 1
5 5 1

Step 2: Counting Distinct Values in an Interval

Once \(F(s)\) is computed, the problem reduces to the well-known problem of “finding the number of distinct values in the interval \([A_j, B_j]\) of the array \(F[1], F[2], \ldots, F[K]\).”

Naive approach: For each query, scan the interval and insert values into a set → \(O(QK)\), which risks TLE.

Efficient approach: Use offline query processing + BIT (Binary Indexed Tree).

Algorithm

We use the classic technique for finding the number of distinct values in an interval.

  1. Sort the queries in ascending order of their right endpoint \(B_j\).
  2. Scan the array \(F\) from left to right (\(s = 1, 2, \ldots, K\)).
  3. When we see \(F[s] = v\):
    • If value \(v\) has previously appeared at position \(\text{last}[v]\), subtract \(-1\) at position \(\text{last}[v]\) in the BIT (remove the old occurrence).
    • Add \(+1\) at position \(s\) in the BIT (record the latest occurrence).
    • Update \(\text{last}[v] = s\).
  4. When the scan position \(s\) reaches a query’s right endpoint \(B_j\), compute the sum over interval \([A_j, B_j]\) using the BIT.

Why this is correct:

For each value \(v\), only its rightmost occurrence within the scanned range has \(+1\) in the BIT. At the time query \([A_j, B_j]\) is processed, the scan over the range \(s \leq B_j\) is complete. If value \(v\) appears at least once in the interval \([A_j, B_j]\), its rightmost occurrence is within \([A_j, B_j]\), so it is correctly counted by the BIT’s range sum.

Complexity

  • Time complexity: \(O(NK + K \log K + Q \log K)\)
    • \(O(NK)\) for computing \(F(s)\)
    • \(O(K \log K)\) for scanning and BIT operations
    • \(O(Q \log K)\) for answering queries
    • Since the constraint guarantees \(NK \leq 10^7\), the overall computation is sufficiently fast
  • Space complexity: \(O(N + K + Q)\)

Implementation Notes

  • BIT (Fenwick Tree) usage: A data structure that supports point updates (\(+1\) / \(-1\)) and range sum queries in \(O(\log K)\). The sum over interval \([l, r]\) is computed as query(r) - query(l-1).

  • Offline processing: By sorting queries by their right endpoint and processing them simultaneously with the scan, we only need to maintain the “latest occurrence position” for each value.

  • last array: For each value \(v\) (\(1 \leq v \leq K\)), records the position \(s\) where \(F[s] = v\) was last seen. The initial value is \(0\) (not yet seen).

    Source Code

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

struct BIT {
    vector<int> tree;
    int n;
    BIT(int n) : n(n), tree(n+1, 0) {}
    void update(int i, int val) {
        for (; i <= n; i += i & (-i))
            tree[i] += val;
    }
    int query(int i) {
        int s = 0;
        for (; i > 0; i -= i & (-i))
            s += tree[i];
        return s;
    }
    int query(int l, int r) {
        return query(r) - query(l-1);
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    int N, K, Q;
    cin >> N >> K >> Q;
    
    vector<int> L(N), R(N), X(N);
    for (int i = 0; i < N; i++) {
        cin >> L[i] >> R[i] >> X[i];
    }
    
    // Compute F[s] for all s from 1 to K
    vector<int> F(K+1);
    for (int s = 1; s <= K; s++) {
        int v = s;
        for (int i = 0; i < N; i++) {
            if (L[i] <= v && v <= R[i]) {
                v = X[i];
            }
        }
        F[s] = v;
    }
    
    // Read queries
    vector<int> A(Q), B(Q), ans(Q);
    vector<pair<int,int>> queries(Q);
    for (int j = 0; j < Q; j++) {
        cin >> A[j] >> B[j];
        queries[j] = {B[j], j};
    }
    
    // Sort queries by right endpoint
    sort(queries.begin(), queries.end());
    
    // Process using BIT (offline, sweep right endpoint)
    BIT bit(K);
    vector<int> last(K+1, 0);
    int qi = 0;
    for (int s = 1; s <= K; s++) {
        int v = F[s];
        if (last[v] > 0) {
            bit.update(last[v], -1);
        }
        bit.update(s, 1);
        last[v] = s;
        
        while (qi < Q && queries[qi].first == s) {
            int j = queries[qi].second;
            ans[j] = bit.query(A[j], B[j]);
            qi++;
        }
    }
    
    for (int j = 0; j < Q; j++) {
        cout << ans[j] << '\n';
    }
    
    return 0;
}

This editorial was generated by claude4.6opus-thinking.

投稿日時:
最終更新: