This commit is contained in:
Zengtudor 2025-07-15 11:59:23 +08:00
parent c483494ac5
commit 85df9dbbf1
3 changed files with 368 additions and 0 deletions

44
src/7/15/U86021.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <cstdint>
#include <iostream>
#include <istream>
#include <map>
#include <stdexcept>
#include <string>
#include <utility>
#include <variant>
#include <vector>
using ll =int64_t;
ll n,m,a,b;
std::vector<std::string> ans;
std::map<std::string, ll> ansm;
std::variant<std::string,std::monostate> 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<std::string>(&findr)){
std::cout<<*ptr<<'\n';//TODO 检查相反串及特殊情况
}else{
}
}

184
src/7/15/U86021.md Normal file
View File

@ -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 <iostream>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <string>
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<string> inputs(n);
for (int i = 0; i < n; i++) {
cin >> inputs[i];
}
// 统计每个答案字符串的出现频率
map<string, int> freq;
for (string s : inputs) {
freq[s]++;
}
if (a > 0 || b > 0) {
set<string> 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<string> forbidden_set;
for (auto &p : freq) {
string s = p.first;
forbidden_set.insert(s);
string opp = opposite(s);
forbidden_set.insert(opp);
}
// 将禁止集合排序
vector<string> 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$ 时也能快速找到最小字典序答案。

140
src/7/15/U86021ds.cpp Normal file
View File

@ -0,0 +1,140 @@
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <string>
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<string> inputs(n);
for (int i = 0; i < n; i++) {
cin >> inputs[i];
}
map<string, int> freq;
for (string s : inputs) {
freq[s]++;
}
if (a > 0 || b > 0) {
set<string> 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<string> forbidden_set;
for (auto &p : freq) {
string s = p.first;
forbidden_set.insert(s);
string opp = oppo(s);
forbidden_set.insert(opp);
}
vector<string> 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;
}