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;
}