T1:Chord

模拟

代码实现
s = input()
if s in 'ACE, BDF, CEG, DFA, EGB, FAC, GBD':
print('Yes')
else:
print('No')

T2:TaK Code

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
bool check(vector<string> s) {
rep(i, 3)rep(j, 3) if (s[i][j] != '#') return false;
rep(i, 3)rep(j, 3) if (s[8-i][8-j] != '#') return false;
rep(i, 4)rep(j, 4) {
if (i < 3 and j < 3) continue;
if (s[i][j] != '.') return false;
if (s[8-i][8-j] != '.') return false;
}
return true;
}
int main() {
int n, m;
cin >> n >> m;
vector<string> s(n);
rep(i, n) cin >> s[i];
rep(si, n-8)rep(sj, m-8) {
vector<string> t(9);
rep(i, 9)rep(j, 9) t[i] += s[si+i][sj+j];
if (check(t)) cout << si+1 << ' ' << sj+1 << '\n';
}
return 0;
}

T3:Invisible Hand

二分答案

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> a(n), b(m);
rep(i, n) cin >> a[i];
rep(i, m) cin >> b[i];
int wa = 0, ac = 1001001001;
while (ac-wa > 1) {
int wj = (ac+wa)/2;
int na = 0, nb = 0;
rep(i, n) if (a[i] <= wj) na++;
rep(i, m) if (b[i] >= wj) nb++;
if (na >= nb) ac = wj; else wa = wj;
}
cout << ac << '\n';
return 0;
}

T4:Count Bracket Sequences

( 看成 +1,将 ) 看成 -1,合法括号序列的前提条件是要保证任意位置处的前缀和 0

然后暴力 dp 即可

dp[i][j] 表示到第 i 个字符为止的前缀和为 j 的合法方案数
最后的答案为 dp[n][0]

时间复杂度为 O(n2)

代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using mint = modint998244353;
int main() {
string s;
cin >> s;
int n = s.size();
vector dp(n+1, vector<mint>(n+1));
dp[0][0] = 1;
rep(i, n) {
rep(j, n) {
if (s[i] != ')') dp[i+1][j+1] += dp[i][j];
if (s[i] != '(' and j > 0) dp[i+1][j-1] += dp[i][j];
}
}
cout << dp[n][0].val() << '\n';
return 0;
}

T5:Tangency of Cuboids

由于用平面将长方体切开后前后块互不干扰,所以可以用 set 来做合并

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
const int M = 101;
int id[M][M][M];
int main() {
int n;
cin >> n;
rep(i, M)rep(j, M)rep(k, M) id[i][j][k] = -1;
rep(i, n) {
int lx, ly, lz; cin >> lx >> ly >> lz;
int rx, ry, rz; cin >> rx >> ry >> rz;
for (int x = lx; x < rx; ++x) {
for (int y = ly; y < ry; ++y) {
for (int z = lz; z < rz; ++z) {
id[x][y][z] = i;
}
}
}
}
vector<unordered_set<int>> st(n);
auto add = [&](int a, int b) {
if (a == -1 or b == -1) return;
if (a == b) return;
st[a].insert(b);
st[b].insert(a);
};
rep(x, M-1)rep(y, M-1)rep(z, M-1) {
add(id[x][y][z], id[x][y][z+1]);
add(id[x][y][z], id[x][y+1][z]);
add(id[x][y][z], id[x+1][y][z]);
}
rep(i, n) cout << st[i].size() << '\n';
return 0;
}

T6:Cans and Openers

如果不考虑不用开罐器就能打开的罐头,那么只需重复购买剩下可使用次数最多的开罐器,然后按满意度从高到低依次购买罐头,就能找到购买 0m 件商品的答案。

接下来,只需枚举所有不用开罐器就能打开的罐头数量,把它们加起来即可!

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
inline void chmin(int& a, int b) { if (a > b) a = b; }
int main() {
int n, m;
cin >> n >> m;
vector<ll> a, b, c;
rep(i, n) {
int t, x;
cin >> t >> x;
if (t == 0) a.push_back(x);
if (t == 1) b.push_back(x);
if (t == 2) c.push_back(x);
}
auto f = [&](vector<ll>& a) {
sort(a.rbegin(), a.rend());
a.resize(m);
a.insert(a.begin(), 0);
rep(i, a.size()-1) a[i+1] += a[i];
};
f(a);
sort(b.rbegin(), b.rend()); b.resize(m);
sort(c.rbegin(), c.rend()); c.resize(m);
ll ans = a[m];
int num = 0, bi = 0, ci = 0;
ll now = 0;
rep(i, m) {
if (num) num--, now += b[bi++];
else num += c[ci++];
ans = max(ans, now+a[m-i-1]);
}
cout << ans << '\n';
return 0;
}

T7:Avoid Straight Line

正难则反

我们可以考虑统计三点落在同一条树链上的方案数,答案为 12(uv(dist(u,v)1)) ,其中 dist(u,v) 表示点 u 到点 v 的最短路

uv(dist(u,v)1)=uvdist(u,v)uv1

12uvdist(u,v)=e经过边e的树链(u,v)的数量,其实就是这道经典题Tree Distance(★5)题解

12uv1=nC2

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
vector<vector<int>> to(n);
rep(i, n-1) {
int a, b;
cin >> a >> b;
--a; --b;
to[a].push_back(b);
to[b].push_back(a);
}
ll ans = (ll)n*(n-1)*(n-2)/6 + (ll)n*(n-1)/2;
auto dfs = [&](auto& f, int v, int p=-1) -> int {
int t = 1;
for (int u : to[v]) {
if (u == p) continue;
int r = f(f, u, v);
ans -= (ll)r*(n-r);
t += r;
}
return t;
};
dfs(dfs, 0);
cout << ans << '\n';
return 0;
}

T8:snukesnuke

容易发现对于最小周期相同的字符串的形式都是 "*" ×x

i 个人的昵称就是从上一个最小周期和 Si 一样且循环次数 x 也一样的昵称的 k+1 开始检查
可以用哈希表来完成记忆化

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
// Morris-Pratt
template<typename T>
struct MP {
int n;
T t;
vector<int> a;
MP() {}
MP(const T& t): t(t) {
n = t.size();
a = vector<int>(n+1);
a[0] = -1;
int j = -1;
rep(i, n) {
while (j != -1 and t[j] != t[i]) j = a[j];
j++;
a[i+1] = j;
}
}
int operator[](int i) { return a[i]; }
vector<int> findAll(const T& s) {
vector<int> res;
int j = 0;
for (int i = 0; i < s.size(); ++i) {
while (j != -1 and t[j] != s[i]) j = a[j];
j++;
if (j == n) {
res.push_back(i-j+1);
j = a[j];
}
}
return res;
}
};
struct Solver {
unordered_set<int> s;
unordered_map<int, int> lk;
Solver() {}
int operator()(int n) {
int k = lk[n]+1;
while (s.count(k*n)) k++;
s.insert(k*n);
lk[n] = k;
return k;
}
};
int main() {
int q;
cin >> q;
map<string, Solver> ss;
rep(qi, q) {
string s;
cin >> s;
int n = s.size();
int c = n;
{
MP mp(s);
c = n-mp[n];
if (n%c) c = n;
}
s = s.substr(0, c);
n /= c;
cout << ss[s](n) << ' ';
}
return 0;
}