「逻辑推理」AtCoder AT_abc401_d D - Logical Filling
前言
这次的 D 题出得很好,不仅融合了数学逻辑推理的知识,还有很多细节值得反复思考。虽然通过人数远高于 E,但是通过率甚至不到 60%,可见这些细节正是出题人的侧重点。
题目大意
给定一个长度为
N
N
N 的字符串
S
S
S,由 o
和 .
组成。现在一些位置的字符不明确,用 ?
表示,可以替换成 o
和 .
中的任意一个。要求目标串(所有位置都被替换之后)同时满足以下两个条件:
-
S
S
S 中有恰好
K
K
K 个
o
。 - 任意两个
o
不相邻。
现在要填这个串,但是因为条件有限,只能完成部分内容。输出所有答案唯一(可以确定)的位置的字符,其他位置仍用 ?
表示。
思路
为了简化问题,我们要先从最简单的位置入手,再解决其他位置。让我们来分析一下都有哪些情况是确定的:
描述 | 结果 | 备注 |
---|---|---|
一个问号与 o 相邻 | 这里填 . | 无 |
o 的数量已经达到
K
K
K | 所有问号处填 . | 无 |
o 的数量与问号的数量之和恰好为
K
K
K | 所有问号处填 o | 无 |
出现连着
2
⋅
M
+
1
2\cdot M+1
2⋅M+1 个问号,且这段里必须填
M
+
1
M+1
M+1 个 o 1 | 这段形如 o.o.o.···.o | 本人赛时曾忽略 |
然后我们执行这些操作无限次,直到无法更新任何地方(执行了之后该轮没有任何格子被更新)为止。
代码
赛时 AC 提交记录:Submission #64790876。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n, k;
string s;
int t[200010];
int main()
{
cin >> n >> k >> s;
s = " " + s + " ";
int upd = 0;
do
{
upd = 0;
for (int i = 1; i <= n; i++)
if (s[i] == '?')
{
if (i != 1 && s[i - 1] == 'o')
s[i] = '.', upd++;
if (i != n && s[i + 1] == 'o')
s[i] = '.', upd++;
}
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= n; i++)
{
if (s[i] == 'o') cnt1++;
if (s[i] == '?') cnt2++;
}
if (cnt1 + cnt2 == k)
{
for (int i = 1; i <= n; i++)
if (s[i] == '?')
s[i] = 'o', upd++;
// break;
}
else if (cnt1 == k)
{
for (int i = 1; i <= n; i++)
if (s[i] == '?')
s[i] = '.';
// break;
}
for (int i = 1; i <= n; i++)
if (s[i] == '?')
{
if (s[i - 1] == 'o')
s[i] = '.', upd++;
if (s[i + 1] == 'o')
s[i] = '.', upd++;
}
int cnt = 0, c = 0, flag = 1;
for (int i = 1; i <= n; i++)
t[i] = 0;
for (int i = 1; i <= n; i++)
{
if (s[i] == '?')
t[i] = t[i - 1] + 1;
else
{
cnt += (t[i - 1] + 1) / 2;
t[i] = 0;
}
}
cnt += (t[n] + 1) / 2;
for (int i = n; i >= 1; i--)
if (t[i] && t[i + 1])
t[i] = t[i + 1];
if (cnt + cnt1 == k)
{
for (int i = 1; i <= n; i++)
{
// cout << t[i] << " " << i << endl;
if (t[i] % 2 == 0) continue;
if (s[i] == '?' && s[i - 1] != 'o')
s[i] = 'o', upd++;
else if (s[i] == '?')
s[i] = '.', upd++;
}
// break;
}
} while (upd != 0);
for (int i = 1; i <= n; i++)
cout << s[i];
return 0;
}
/*
7 3
?o????.
---
10 5
?????.????
*/
如何判定这种情况?当前仅当数组中每一段连续的问号(长度为 L L L)都填 ⌈ L 2 ⌉ \left \lceil \frac{L}{2} \right \rceil ⌈2L⌉ 个
o
时o
的数量恰好为 K K K。 ↩︎