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
,合法括号序列的前提条件是要保证任意位置处的前缀和 \(\geqslant 0\)
然后暴力 \(\operatorname{dp}\) 即可
记 dp[i][j]
表示到第 \(i\) 个字符为止的前缀和为 \(j\) 的合法方案数
最后的答案为 \(\operatorname{dp}[n][0]\)
时间复杂度为 \(\mathcal{O}(n^2)\)
代码实现
#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
如果不考虑不用开罐器就能打开的罐头,那么只需重复购买剩下可使用次数最多的开罐器,然后按满意度从高到低依次购买罐头,就能找到购买 \(0 \sim m\) 件商品的答案。
接下来,只需枚举所有不用开罐器就能打开的罐头数量,把它们加起来即可!
代码实现
#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
正难则反
我们可以考虑统计三点落在同一条树链上的方案数,答案为 \(\frac{1}{2}\left(\sum\limits_{u \neq v} \big(\operatorname{dist}(u, v)-1\big)\right)\) ,其中 \(\operatorname{dist}(u, v)\) 表示点 \(u\) 到点 \(v\) 的最短路
\( \sum\limits_{u \neq v} \big(\operatorname{dist}(u, v)-1\big) = \sum\limits_{u \neq v} \operatorname{dist}(u, v) - \sum\limits_{u \neq v} 1 \)
\( \frac{1}{2}\sum\limits_{u \neq v} \operatorname{dist}(u, v) = \sum\limits_e \text{经过边}e\text{的树链}(u, v)\text{的数量} \),其实就是这道经典题Tree Distance(★5),题解
\(\frac{1}{2}\sum\limits_{u \neq v} 1 = {_n}C_2\)
代码实现
#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
容易发现对于最小周期相同的字符串的形式都是 "*" \(\times x\)
第 \(i\) 个人的昵称就是从上一个最小周期和 \(S_i\) 一样且循环次数 \(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;
}