1 条题解
-
0
思路与算法
按轮次DP,每个结尾值只保留至多两个不同末轮人物编号,足以判断下一人是否可用。扫描每人词库,用长度k-1的滑动窗口统计合格开头位置,产生本轮终点与人物编号。
正确性说明
对下一人物p,只需知道上轮这个值是否存在一个人物不等于p:若已有两不同人物必可,只有一个则比较即可,所以保留两个编号不损失信息。当前末位置j可用的开头恰在[j-k+1,j-1]且其值满足上轮人物条件,滑动窗口计数精确覆盖所有长度2..k连续子序列。按轮更新数组、回答该轮询问归纳得正确结果。
复杂度
每组O(R*(总词库长度+n+V)+q)时间、O(总词库长度+n+q+V)空间,R<=100、V=200001;含每轮四人物数组清空和询问分桶初始化。
易错点
本轮必须只读取上一轮固定数组,不能把当前新状态继续用于同一轮;同一人可以隔轮再次使用,但不能相邻两轮使用。重复字符的不同出现位置均须纳入窗口。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n, k, q; cin >> n >> k >> q; vector<vector<int>> s(n); for (auto &a : s) { int l; cin >> l; a.resize(l); for (int &v : a) cin >> v; } vector<vector<pair<int, int>>> queries(101); vector<int> ans(q); int maxr = 0; for (int i = 0; i < q; i++) { int r, c; cin >> r >> c; queries[r].push_back({c, i}); maxr = max(maxr, r); } const int V = 200001; vector<int> a(V, -1), b(V, -1), c(V, -1), d(V, -1); a[1] = 0; for (int r = 1; r <= maxr; r++) { fill(c.begin(), c.end(), -1); fill(d.begin(), d.end(), -1); for (int p = 0; p < n; p++) { int who = p + 1, count = 0; auto ok = [&](int v) { return a[v] >= 0 && (a[v] != who || b[v] >= 0); }; for (int i = 0; i < (int)s[p].size(); i++) { if (i > 0) count += ok(s[p][i - 1]); if (i >= k) count -= ok(s[p][i - k]); if (count) { int v = s[p][i]; if (c[v] < 0) c[v] = who; else if (c[v] != who) d[v] = who; } } } for (auto x : queries[r]) ans[x.second] = c[x.first] >= 0; a.swap(c); b.swap(d); } for (int v : ans) cout << v << "\n"; } }
- 1