diff --git a/src/7/15/U86021.cpp b/src/7/15/U86021.cpp new file mode 100644 index 0000000..89ae039 --- /dev/null +++ b/src/7/15/U86021.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ll =int64_t; +ll n,m,a,b; +std::vector ans; +std::map ansm; + +std::variant findAnsFromNum(ll n){ + for(auto &s:ansm){ + if(s.second==n){ + return s.first; + } + } + return std::monostate(); +} + +int main(){ + std::iostream::sync_with_stdio(false); + std::cin.tie(nullptr); + std::cout.tie(nullptr); + + std::cin>>n>>m>>a>>b; + ans.resize(n+1); + for(ll i=1;i<=n;i++){ + std::string s; + std::cin>>s; + ansm[s]++; + ans[i]=std::move(s); + } + auto findr = findAnsFromNum(a); + if(std::string* ptr = std::get_if(&findr)){ + std::cout<<*ptr<<'\n';//TODO 检查相反串及特殊情况 + }else{ + + } +} \ No newline at end of file diff --git a/src/7/15/U86021.md b/src/7/15/U86021.md new file mode 100644 index 0000000..b45ac87 --- /dev/null +++ b/src/7/15/U86021.md @@ -0,0 +1,184 @@ +### 题目分析 +May 的班级有 $n$ 位同学,考试包含 $m$ 道判断题。每位同学的答案是一个长度为 $m$ 的字符串(仅包含 'N' 或 'Y')。老师宣布有 $a$ 位同学满分(答案完全正确),$b$ 位同学 0 分(答案完全错误),其余同学既不是满分也不是 0 分。May 知道正确答案,但需要你输出字典序最小的正确答案字符串,若无解则输出 `-1`。 + +### 解题思路 +1. **问题分析**: + - 满分条件:答案字符串与正确答案完全相同。 + - 0 分条件:答案字符串与正确答案完全相反(即每位答案都相反)。 + - 需要找到正确答案 $s$,使得: + - 恰好 $a$ 个同学的答案等于 $s$。 + - 恰好 $b$ 个同学的答案等于 $s$ 的相反串 $t$。 + - 其余同学的答案既不是 $s$ 也不是 $t$。 + +2. **关键观察**: + - 如果 $a > 0$,则正确答案 $s$ 必须在输入中出现,且出现次数恰好为 $a$。 + - 如果 $b > 0$,则 $s$ 的相反串 $t$ 必须在输入中出现,且出现次数恰好为 $b$。 + - 如果 $a = 0$ 且 $b > 0$,则 $s$ 不在输入中,但 $t$ 在输入中出现次数为 $b$。 + - 如果 $a = b = 0$,则 $s$ 及其相反串 $t$ 都不在输入中,需要找到字典序最小的满足条件的 $s$。 + +3. **算法选择**: + - **统计频率**:使用 `map` 统计每个答案字符串的出现次数。 + - **处理 $a > 0$ 或 $b > 0$ 的情况**: + - 枚举出现次数为 $a$ 的字符串 $s$,检查其相反串 $t$ 的出现次数是否满足 $b$ 的条件。 + - 枚举出现次数为 $b$ 的字符串 $t$,生成 $s$ 为 $t$ 的相反串,并检查 $s$ 不在输入中(当 $a=0$)。 + - **处理 $a = b = 0$ 的情况**: + - 构建禁止集合 `forbidden_set`,包含输入中所有字符串及其相反串。 + - 按字典序排序禁止集合。 + - 从全 'N' 的字符串开始,按字典序递增寻找第一个不在禁止集合中的字符串。 + +4. **复杂度分析**: + - 统计频率:$O(n \cdot m)$。 + - 枚举候选答案:$O(k \cdot m)$,其中 $k$ 是去重后的字符串数量($k \leq n$)。 + - 处理 $a = b = 0$:排序禁止集合 $O(n \log n \cdot m)$,扫描 $O(n \cdot m)$。 + - 总体复杂度:$O(n \cdot m)$,满足数据规模要求。 + +### 代码实现 +```cpp +#include +#include +#include +#include +#include +#include +using namespace std; + +// 生成字符串的相反串 +string opposite(string s) { + string res = s; + for (char &c : res) { + if (c == 'N') + c = 'Y'; + else + c = 'N'; + } + return res; +} + +// 计算字典序下一个字符串 +string next_string(string s, int m) { + string res = s; + int i = m - 1; + while (i >= 0 && res[i] == 'Y') { + res[i] = 'N'; + i--; + } + if (i >= 0) { + res[i] = 'Y'; + return res; + } else { + return ""; // 溢出,无下一个字符串 + } +} + +int main() { + int n, m, a, b; + cin >> n >> m >> a >> b; + vector inputs(n); + for (int i = 0; i < n; i++) { + cin >> inputs[i]; + } + + // 统计每个答案字符串的出现频率 + map freq; + for (string s : inputs) { + freq[s]++; + } + + if (a > 0 || b > 0) { + set candidates; + + // 处理 a > 0 的情况 + if (a > 0) { + for (auto &p : freq) { + string s = p.first; + int cnt = p.second; + if (cnt == a) { + string opp = opposite(s); + if (b > 0) { + // 检查相反串出现次数是否为 b + if (freq.count(opp) && freq[opp] == b) { + candidates.insert(s); + } + } else { + // 检查相反串不在输入中 + if (!freq.count(opp)) { + candidates.insert(s); + } + } + } + } + } + + // 处理 b > 0 且 a = 0 的情况 + if (b > 0 && a == 0) { + for (auto &p : freq) { + string t = p.first; + int cnt = p.second; + if (cnt == b) { + string s_candidate = opposite(t); + // 检查 s_candidate 不在输入中 + if (!freq.count(s_candidate)) { + candidates.insert(s_candidate); + } + } + } + } + + // 输出字典序最小的候选答案 + if (candidates.empty()) { + cout << "-1" << endl; + } else { + cout << *candidates.begin() << endl; + } + } else { + // 处理 a = 0 且 b = 0 的情况 + set forbidden_set; + for (auto &p : freq) { + string s = p.first; + forbidden_set.insert(s); + string opp = opposite(s); + forbidden_set.insert(opp); + } + + // 将禁止集合排序 + vector forbidden_vec(forbidden_set.begin(), forbidden_set.end()); + sort(forbidden_vec.begin(), forbidden_vec.end()); + + string candidate = string(m, 'N'); // 从全 'N' 开始 + bool valid = true; + int i = 0; + while (i < forbidden_vec.size()) { + if (candidate < forbidden_vec[i]) { + break; // 找到不在禁止集合中的字符串 + } else if (candidate > forbidden_vec[i]) { + i++; // 跳过较小的禁止字符串 + } else { + candidate = next_string(candidate, m); // 生成下一个字符串 + if (candidate == "") { + valid = false; // 无有效下一个字符串 + break; + } + i++; + } + } + + if (!valid) { + cout << "-1" << endl; + } else { + cout << candidate << endl; + } + } + + return 0; +} +``` + +### 代码解释 +1. **输入处理**:读取同学数量 $n$、题目数量 $m$、满分人数 $a$、0 分人数 $b$ 以及所有同学的答案字符串。 +2. **频率统计**:使用 `map` 统计每个答案字符串的出现次数。 +3. **候选答案生成**: + - 当 $a > 0$ 或 $b > 0$ 时,枚举满足条件的字符串(出现次数为 $a$ 或 $b$),并检查其相反串的条件。 + - 当 $a = b = 0$ 时,构建禁止集合并按字典序排序,从全 'N' 的字符串开始,按字典序递增寻找第一个不在禁止集合中的字符串。 +4. **输出结果**:若有候选答案,输出字典序最小的;若无解,输出 `-1`。 + +此解法高效处理了各种情况,特别是利用字典序递增生成字符串的方法,确保在 $a = b = 0$ 时也能快速找到最小字典序答案。 \ No newline at end of file diff --git a/src/7/15/U86021ds.cpp b/src/7/15/U86021ds.cpp new file mode 100644 index 0000000..e97af27 --- /dev/null +++ b/src/7/15/U86021ds.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +using namespace std; + + +string oppo(string s) { + string res = s; + for (char &c : res) { + if (c == 'N') + c = 'Y'; + else + c = 'N'; + } + return res; +} + + +string next_string(string s, int m) { + string res = s; + int i = m - 1; + while (i >= 0 && res[i] == 'Y') { + res[i] = 'N'; + i--; + } + if (i >= 0) { + res[i] = 'Y'; + return res; + } else { + return ""; + } +} + +int main() { + std::iostream::sync_with_stdio(false); + std::cin.tie(nullptr); + std::cout.tie(nullptr); + int n, m, a, b; + cin >> n >> m >> a >> b; + vector inputs(n); + for (int i = 0; i < n; i++) { + cin >> inputs[i]; + } + + + map freq; + for (string s : inputs) { + freq[s]++; + } + + if (a > 0 || b > 0) { + set candidates; + + + if (a > 0) { + for (auto &p : freq) { + string s = p.first; + int cnt = p.second; + if (cnt == a) { + string opp = oppo(s); + if (b > 0) { + + if (freq.count(opp) && freq[opp] == b) { + candidates.insert(s); + } + } else { + + if (!freq.count(opp)) { + candidates.insert(s); + } + } + } + } + } + + + if (b > 0 && a == 0) { + for (auto &p : freq) { + string t = p.first; + int cnt = p.second; + if (cnt == b) { + string s_candidate = oppo(t); + + if (!freq.count(s_candidate)) { + candidates.insert(s_candidate); + } + } + } + } + + + if (candidates.empty()) { + cout << "-1" << endl; + } else { + cout << *candidates.begin() << endl; + } + } else { + + set forbidden_set; + for (auto &p : freq) { + string s = p.first; + forbidden_set.insert(s); + string opp = oppo(s); + forbidden_set.insert(opp); + } + + + vector forbidden_vec(forbidden_set.begin(), forbidden_set.end()); + sort(forbidden_vec.begin(), forbidden_vec.end()); + + string candidate = string(m, 'N'); + bool valid = true; + int i = 0; + while (i < forbidden_vec.size()) { + if (candidate < forbidden_vec[i]) { + break; + } else if (candidate > forbidden_vec[i]) { + i++; + } else { + candidate = next_string(candidate, m); + if (candidate == "") { + valid = false; + break; + } + i++; + } + } + + if (!valid) { + cout << "-1" << endl; + } else { + cout << candidate << endl; + } + } + + return 0; +} \ No newline at end of file