Quiz Competition: Qualifiers

Quiz Competition: Qualifiers

题意简述

有 \(N\) 个参与者,最多 \(M\) 人能通过预选赛。预选赛有 \(K\) 道二选一题,答案 o 或 x。
第 \(i\) 个参与者的答案为字符串 \(S_i\),正确答案为 \(T\)。

晋级过程按 \(k = 1,2,\dots,K\) 顺序进行:

  • 设当前未确定集合为 \(U\),已晋级人数为 \(pass\)。
  • 令 \(c\) 为 \(U\) 中第 \(k\) 题答对的人数。
  • 若 \(pass + c \le M\),则 \(U\) 中第 \(k\) 题答对的人全部晋级,\(U\) 变为 \(U\) 中第 \(k\) 题答错的人。
  • 否则,\(U\) 中第 \(k\) 题答错的人全部淘汰,\(U\) 变为 \(U\) 中第 \(k\) 题答对的人。

最后仍留在 \(U\) 中的人淘汰。

有 \(Q\) 次修改,每次翻转某个参与者的某题答案,然后询问该参与者是否晋级。

约束:

  • \(1 \le M \le N \le 3 \times 10^4\)
  • \(1 \le K \le 200\)
  • \(1 \le Q \le 5 \times 10^4\)

核心转化

把每个参与者的答题情况转化为一个长度为 \(K\) 的 01 串:

\[bit[i][k] = \begin{cases} 1, & S_i[k] = T[k] \\ 0, & S_i[k] \ne T[k] \end{cases} \]

修改某题答案,等价于翻转 \(bit[i][j]\)。

整个晋级过程中,未确定集合 \(U\) 始终是满足某个「前 \(k\) 题 01 模式」的参与者集合。
这个结构天然对应一棵 Trie 树。


Trie 树维护

将所有参与者的 \(bit[i]\) 插入 Trie,根到叶子的路径长度为 \(K\)。

每个节点维护:

  • cnt:该节点子树中参与者数量。

于是:

  • ch[1][cur] 的 cnt 表示:满足当前前缀条件且下一题答对的人数。
  • ch[0][cur] 的 cnt 表示:满足当前前缀条件且下一题答错的人数。
#include <bits/stdc++.h>
using namespace std;
using vvi = vector<vector<int>>;
using ll = long long;
const int N = 16000005;
int ch[2][N], cnt[N];
int idx = 0;

void solve() {
    int n, m, k; cin >> n >> m >> k;
    string T; cin >> T;
    vector<vector<uint8_t>> pari(n, vector<uint8_t>(k));
    for(int i = 0; i < n; i ++) {
        string s; cin >> s;
        for(int j = 0; j < k; j ++) pari[i][j] = (s[j] == T[j]);
    }

    ch[0][0] = ch[1][0] = -1;
    for(int i = 0; i < n; i++) {
        int cur = 0;
        cnt[cur] ++;
        for(int j = 0; j < k; j ++) {
            int b = pari[i][j];
            if(ch[b][cur] == -1) {
                idx ++;
                ch[0][idx] = ch[1][idx] = -1;
                ch[b][cur] = idx;
            } 
            cur = ch[b][cur];
            cnt[cur] ++;
        }
    }

    int q; cin >> q;
    while(q --) {
        int x, y; cin >> x >> y; x --, y --;

        int cur = 0;
        for(int j = 0; j < k; j ++) {
            cur = ch[pari[x][j]][cur];
            cnt[cur] --;
        }

        pari[x][y] ^= 1;

        cur = 0;
        for(int j = 0; j < k; j ++) {
            int b = pari[x][j];
            if(ch[b][cur] == -1) {
                idx ++;
                ch[0][idx] = ch[1][idx] = -1;
                ch[b][cur] = idx;
            }
            else if(cnt[ch[b][cur]] == 0) ch[0][ch[b][cur]] = ch[1][ch[b][cur]] = -1;
            cur = ch[b][cur];
            cnt[cur] ++;
        }

        cur = 0;
        int pass = 0;
        int flag = -1;// -1未定 0淘汰 1
        for(int j = 0; j < k; j ++) {
            if(cur == -1) break;
            int c = (ch[1][cur] != -1 ? cnt[ch[1][cur]] : 0);
            if(pass + c <= m) {
                pass += c;
                cur = ch[0][cur];
                if(flag == -1 && pari[x][j] == 1) flag = 1;
            }
            else {
                cur = ch[1][cur];
                if(flag == -1 && pari[x][j] == 0) flag = 0;
            }
        }
        cout << (flag == 1 ? "Yes\n" : "No\n");
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int _=1; //cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-11-03 22:00  thenrify  阅读(3)  评论(0)    收藏  举报