Codeforces Round #306 (Div. 2) 题解
旅行传送门
一些闲话:看着难度适中就拿来练手了,没想到居然能AK,顺便纪念下第一次独自敲出2200的题。
A. Two Substrings
题意:给你一个字符串 \(s\) ,问 \(s\) 中是否同时包含不重叠的子串 \(AB\) 和 \(BA\) 。
题目分析:扫两遍,第一次先找 \(AB\) 再找 \(BA\) ,第二次先找 \(BA\) 再找 \(AB\) ,任意一次满足即符合要求。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr);
using namespace std;
bool check1(string s)
{
int n = s.length() - 1, f1 = 0, f2 = 0;
for (int i = 0; i < n; i++)
if (s[i] == 'A' && s[i + 1] == 'B')
{
f1 = 1;
s[i] = s[i + 1] = 'C';
break;
}
for (int i = 0; i < n; i++)
if (s[i] == 'B' && s[i + 1] == 'A')
{
f2 = 1;
break;
}
return f1 & f2;
}
bool check2(string s)
{
int n = s.length() - 1, f1 = 0, f2 = 0;
for (int i = 0; i < n; i++)
if (s[i] == 'B' && s[i + 1] == 'A')
{
f1 = 1;
s[i] = s[i + 1] = 'C';
break;
}
for (int i = 0; i < n; i++)
if (s[i] == 'A' && s[i + 1] == 'B')
{
f2 = 1;
break;
}
return f1 & f2;
}
int main(int argc, char const *argv[])
{
IOS;
string s;
cin >> s;
puts(check1(s) | check2(s) ? "YES" : "NO");
return 0;
}
B. Preparing Olympiad
题意:给你 \(n\) 个数,你可以从中挑选任意个放进集合中,问有多少种放法使得集合满足:
- 集合中的元素之和 \(sum \in [l,r]\)
- 集合中元素的最大值与最小值之差 \(\leq x\)
题目分析:\(n \leq 15\) ,暴力 \(dfs\) 即可。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int inf = 0x3f3f3f3f;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, l, r, x, cnt;
std::vector<int> a(20), vis(20);
void dfs(int cur, int mx, int mn, int pre)
{
if (cur > r)
return;
if (cur >= l && mx - mn >= x)
++cnt;
for (int i = pre + 1; i <= n; i++)
{
if (!vis[i])
{
vis[i] = 1;
cur += a[i];
int m1 = std::max(a[i], mx);
int m2 = std::min(a[i], mn);
dfs(cur, m1, m2, i);
vis[i] = 0;
cur -= a[i];
}
}
}
int main(int argc, char const *argv[])
{
n = read(), l = read(), r = read(), x = read();
rep(i, 1, n) a[i] = read();
dfs(0, 0, inf, 0);
printf("%d\n", cnt);
return 0;
}
C. Divisibility by Eight
题意: 给你一个数 \(n\) ,问能否删除其中若干位使得其能被8整除?
题目分析:因为 \(1000 ÷ 8 = 125\) ,所以整千数都是 \(8\) 的倍数,而原数减去后三位就是整千数,是 \(8\) 的倍数。因此原数是不是 \(8\) 的倍数,只要看后三位是不是 \(8\) 的倍数,那么我们只需特判一二位,三位以上只需看后三位即可。
顺便做个笔记:
-
若一个整数的末位是偶数,则这个数能被 \(2\) 整除。
-
若一个整数的数字和能被 \(3\) 整除,则这个整数能被 \(3\) 整除
-
若一个整数的末尾两位数能被 \(4\) 整除,则这个数能被 \(4\) 整除
-
若一个整数的末位是 \(0\) 或 \(5\) ,则这个数能被 \(5\) 整除
-
若一个整数能被 \(2\) 和 \(3\) 整除,则这个数能被 \(6\) 整除
-
若一个整数的个位数字截去,再从余下的数中,减去个位数的 \(2\) 倍,如果差是 \(7\) 的倍数,则原数能被 \(7\) 整除。如果差太大或不易看出是否 \(7\) 的倍数,就需要继续上述「截尾、倍大、相减、验差」的过程,直到能清楚判断为止。例如,判断 \(133\) 是否 \(7\) 的倍数的过程如下: \(13-3×2=7\) ,所以 \(133\) 是 \(7\) 的倍数;又例如判断 \(6139\) 是否 \(7\) 的倍数的过程如下: \(613-9×2=595\) , \(59-5×2=49\) ,所以 \(6139\) 是 \(7\) 的倍数,以此类推。
-
若一个整数的末尾三位数能被 \(8\) 整除,则这个数能被 \(8\) 整除
-
若一个整数的数字和能被 \(9\) 整除,则者个整数能被 \(9\) 整除
-
若一个整数末位是 \(0\) ,则这个数能被 \(10\) 整除
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr);
using namespace std;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int main(int argc, char const *argv[])
{
IOS;
string s;
cin >> s;
int n = s.length(), flag = 0;
for (auto ch : s)
{
if (ch - '0' == 8 || ch - '0' == 0)
{
cout << "YES" << endl;
cout << ch << endl;
return 0;
}
if (!(ch - '0' & 1))
flag = 1;
}
if (!flag || n == 1)
{
cout << "NO" << endl;
return 0;
}
if (n == 2)
{
int num = 10 * (s[0] - '0') + s[1] - '0';
if (num % 8)
cout << "NO" << endl;
else
{
cout << "YES" << endl;
cout << num << endl;
}
return 0;
}
else
{
rep(i, 0, n - 1)
{
int num = s[i] - '0';
rep(j, i + 1, n - 1)
{
num = num * 10 + s[j] - '0';
if (!(num % 8))
{
cout << "YES" << endl;
cout << num << endl;
return 0;
}
rep(k, j + 1, n - 1)
{
num = num * 10 + s[k] - '0';
if (!(num % 8))
{
cout << "YES" << endl;
cout << num << endl;
return 0;
}
num /= 10;
}
num /= 10;
}
}
cout << "NO" << endl;
}
return 0;
}
D. Regular Bridge
题意:要求构造出一张无向图,图中每个顶点度数均为 \(k\) ,且至少存在一条桥边,不存在重边和自环。问能否构造出这样的图?若能则输出构造方案。
题目分析:瞎猜 + 乱搞过的,具体证明和构造方案可以参考这位juju的博客:旅行传送门,思路是一样的(原谅我过低的语文水平)。
稍微解释下就是:题目要求至少存在一条桥边,那么往简单了去想,只需构建一条桥,对桥两边的连通块对称的构造就好。由于桥的存在,桥边的两个端点已经有了 \(1\) 的度数,所以我们再分别添加 \(k-1\) 个点连接这两个端点使得其度数等于 \(k\) ,此时这些端点的度数也为 \(1\) ,那就重复之前的步骤再分别添加 \(k-1\) 个点和它们相连,最后新添加的点度数为 \(k-1\) ,那么只需在每两个点间再连一条边即可。
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
void solve(int k)
{
if (!(k & 1))
{
puts("NO");
return;
}
puts("YES");
printf("%d %d\n", 4 * k - 2, k * (2 * k - 1));
for (int i = 1; i <= k; i++)
for (int j = 1; j <= k - 1; j++)
printf("%d %d\n", i, k + j);
for (int i = 2; i <= k; i += 2)
printf("%d %d\n", i, i + 1);
for (int i = 2 * k; i <= 3 * k - 1; i++)
for (int j = 1; j <= k - 1; j++)
printf("%d %d\n", i, 3 * k + j - 1);
for (int i = 2 * k + 1; i <= 3 * k - 1; i += 2)
printf("%d %d\n", i, i + 1);
printf("%d %d\n", 1, 2 * k);
}
int main(int argc, char const *argv[])
{
int k;
scanf("%d", &k);
solve(k);
return 0;
}
E. Brackets in Implications
题意:给你 \(n\) 个数,你可以任意增加括号改变运算顺序,问是否可以构造结果为 \(0\) 的蕴含式。
题目分析:首先明确一点,蕴含式当且仅当 \(1 \rightarrow 0\) 时结果才为 \(0\) ,因此只有序列最后一位为 \(0\) ,答案才可能为 \(0\) 。然后我们从 \(n-1\) 位向前推,只需要再找到一个子序列能通过构造得到 \(1\) ,此时序列为 \((... \rightarrow 1 \rightarrow 0)\) ,那么不管前面 \(...\) 一段是什么,其蕴含 \(1\) 的结果也必定为 \(1\) ,解决方案必定存在,那么我们怎么找到这样的一个子序列呢?分两种情况讨论:
- \(a_{n-2}\) 蕴含 \(a_{n-1}\) 本来就为 \(1\)
- \(a_{n-2}\) 蕴含 \(a_{n-1}\) 为 \(0\) ,即 \(a_{n-2}\) 与 \(a_{n-1}\) 分别为 \(1\) \(0\) ,那就继续往前找直至找到 \(0\) ,即 \(0 \rightarrow 1 \rightarrow 1 \rightarrow 1 \rightarrow ... \rightarrow 1 \rightarrow 1 \rightarrow 0\) ,从而构造出计算结果为 \(1\) 的子序列
AC代码:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int main(int argc, char const *argv[])
{
int n = read();
std::vector<int> a(n + 1), l(n + 1), r(n + 1);
rep(i, 1, n) a[i] = read();
if (n == 1)
{
if (a[1])
puts("NO");
else
{
puts("YES");
printf("0");
}
}
else if (n == 2)
{
if (a[1] == 1 && a[2] == 0)
{
puts("YES");
printf("1->0");
}
else
puts("NO");
}
else
{
if (a[n])
{
puts("NO");
return 0;
}
int flag = 0, pos;
if (a[n - 1] == 0 && a[n - 2] == 1)
{
++r[n - 1], ++l[n - 2];
int p = n - 3;
while (p > 0)
{
++r[n - 1], ++l[p];
if (!a[p])
{
flag = 1, pos = p - 1;
break;
}
--p;
}
}
else
{
++r[n - 1], ++l[n - 2];
flag = 1, pos = n - 3;
}
if (!flag)
{
puts("NO");
return 0;
}
++l[1], ++r[n];
puts("YES");
rep(i, 1, n)
{
while (l[i]--)
printf("(");
printf("%d", a[i]);
if (r[i])
{
while (r[i]--)
printf(")");
if (i ^ n)
printf("->");
}
else
printf("->");
}
}
return 0;
}