公式

D - ウイルス検査と感染端末 / Virus Testing and Infected Terminals 解説 by admin

GPT 5.2 High

Overview

Assuming each terminal is either “infected” or “not infected,” we find the assignment that is consistent with all scan results (1 if the scanned set contains at least one infected terminal, 0 otherwise) and minimizes the number of infected terminals.

Analysis

The key insight of this problem is that all conditions on terminal sets can be expressed as “whether the intersection of the set and the infected set is empty or not.”

  • Scan result \(R_j = 0\) (no infection)
    → The set \(S_j\) examined by this scan contains no infected terminals at all
    → For the infected set \(X\): \(X \cap S_j = \emptyset\)
    → In other words, all terminals in \(S_j\) are confirmed to be non-infected.

  • Scan result \(R_j = 1\) (infection found)
    → The set \(S_j\) contains at least one infected terminal
    \(X \cap S_j \neq \emptyset\)

Since the constraint \(N \le 16\) is small, we can exhaustively search all possible infected terminal sets \(X\) (\(2^N\) possibilities).
A naive “enumerate all and check all conditions” approach is fast enough, but we can speed it up further with the following:

  1. Process all \(R_j=0\) conditions together and immediately exclude terminals from infection candidates (powerful pruning)
  2. Skip candidates with infection count greater than or equal to the current minimum (best) (count-based pruning)
  3. Represent sets as bitmasks for fast intersection checks (x & mask)

Algorithm

Using bitmasks, we represent the infected terminal set \(X\) as an integer from \(0\) to \((1<<N)-1\) (bit \(i\) is 1 if terminal \(i\) is infected).

  1. Read input and convert each scan’s target set to a bitmask mask.
  2. For scans with \(R=0\):
    • Since the set contains no infected terminals, accumulate the entire set into must_zero using OR.
    • Terminals whose bits are set in must_zero cannot be infected.
  3. For scans with \(R=1\):
    • The condition “\(X \cap S_j \neq \emptyset\)” must be satisfied, so save mask into r1_masks.
  4. Enumerate all \(X\) (\(0 \le X < 2^N\)) and find the one with the minimum infection count that satisfies:
    • (X & must_zero) == 0 (no terminal marked as non-infectable is infected)
    • For all m in r1_masks: (X & m) != 0 (intersects with each “infection found” set)

Count the number of infected terminals using popcount (in Python, bit_count()), and update the minimum.

Complexity

  • Time complexity: \(O(2^N \cdot M)\)
    (Since \(N \le 16\), there are at most \(2^{16}=65536\) candidates, and each candidate checks at most \(M\) conditions)
  • Space complexity: \(O(M)\)
    (For storing the scan sets with \(R=1\), plus a constant number of variables)

Implementation Notes

  • Terminal numbers in the input are \(1\)-indexed, so subtract \(1\) before setting the bit with 1 << s.

  • By consolidating \(R=0\) conditions into must_zero, during enumeration a single check (X & must_zero) suffices to eliminate invalid candidates, which is fast.

  • Candidates with infection count greater than or equal to the already found minimum best are unnecessary, so we prune after bit_count().

  • Intersection checks are done simply by checking whether X & mask is 0 or not, which is significantly faster than storing sets as lists.

    Source Code

import sys

def main():
    it = iter(map(int, sys.stdin.buffer.read().split()))
    N = next(it)
    M = next(it)

    must_zero = 0
    r1_masks = []

    for _ in range(M):
        K = next(it)
        mask = 0
        for _ in range(K):
            s = next(it) - 1
            mask |= 1 << s
        r = next(it)
        if r == 0:
            must_zero |= mask
        else:
            r1_masks.append(mask)

    best = N + 1
    for x in range(1 << N):
        if x & must_zero:
            continue
        bc = x.bit_count()
        if bc >= best:
            continue
        ok = True
        for m in r1_masks:
            if (x & m) == 0:
                ok = False
                break
        if ok:
            best = bc
            if best == 0:
                break

    print(best)

if __name__ == "__main__":
    main()

This editorial was generated by gpt-5.2-high.

投稿日時:
最終更新: