A. Typical Interview Problem
题意描述
给你一个字符串评判机制,每次询问给个字符串,问是否符合要求。
简要分析
不难发现 FBFFBFFB 是循环节,只需判断 s s s 是否在有上述循环节组成的字符串中即可。
代码实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 7;
const ll INF = 1e9 + 7, MOD = 998244353;
inline ll read() {
char cCc;
ll xXx = 0, wWw = 1;
while (cCc < '0' || cCc > '9')
(cCc == '-') && (wWw = -wWw), cCc = getchar();
while (cCc >= '0' && cCc <= '9')
xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
xXx *= wWw;
return xXx;
}
string t = "FBFFBFFBFBFFBFFBFBFFBFFBFBFFBFFB";
void solve() {
ll n;
string s;
cin >> n >> s;
if (t.find(s) != string::npos) cout << "Yes" << '\n';
else cout << "No" << '\n';
}
signed main() {
// freopen("code.in","r",stdin);
// freopen("code.out","w",stdout);
ll T = read();
while (T--) solve();
return 0;
}
B. Asterisk-Minor Template
题意描述
给你两个字符串 S , T S,T S,T,你需要给出一个由 * 与小写字母组成的字符串,需要满足以下条件:
- 其中个数小于等于小写字母的个数。
- 使用若干小写字母代替 * 使得与 S S S 相同,每个 * 可以用不同的若干小写字母代替。
- 使用若干小写字母代替 * 使得与 T T T 相同,每个 * 可以用不同的若干小写字母代替。
没有满足条件的输出 NO,否需要先输出 YES。
简要分析
不按发现,需要给出的字符串要么是 X ∗ X* X∗, ∗ X *X ∗X 或 ∗ X ∗ *X* ∗X∗,其中 X X X 代表若干小写字母,对于第三者 X X X 的长度必须大于等于 2 2 2。
随后,分类讨论即可。
代码实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 7;
const ll INF = 1e9 + 7, MOD = 998244353;
inline ll read() {
char cCc;
ll xXx = 0, wWw = 1;
while (cCc < '0' || cCc > '9')
(cCc == '-') && (wWw = -wWw), cCc = getchar();
while (cCc >= '0' && cCc <= '9')
xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
xXx *= wWw;
return xXx;
}
inline void write(ll xXx) {
if (xXx < 0)
putchar('-'), xXx = -xXx;
if (xXx > 9)
write(xXx / 10);
putchar(xXx % 10 + '0');
}
void solve() {
string s, t;
cin >> s >> t;
if (s[0] == t[0]) {
cout << "YES\n";
cout << s[0] << "*\n";
} else if (s[s.size() - 1] == t[t.size() - 1]) {
cout << "YES\n" << "*" << t[t.size() - 1] << '\n';
} else {
bool flag = false;
set<string> v1;
for (ll i = 0; i + 1 < s.size(); i++) {
v1.insert(s.substr(i, 2));
}
for (ll i = 0; i + 1 < t.size(); i++) {
if (v1.contains(t.substr(i, 2))) {
cout << "YES\n" << '*' << t.substr(i, 2) << "*\n";
flag = true;
break;
}
}
if(!flag)cout << "NO\n";
}
}
signed main() {
// freopen("code.in","r",stdin);
// freopen("code.out","w",stdout);
ll T = read();
while (T--)solve();
return 0;
}
C. Maximum Set
题意描述
给你一个区间,求一个集合 S , S ∈ [ l , r ] S,S\in[l,r] S,S∈[l,r] 的最大长度与最长集合的个数。
需要满足 ∀ i , j ∈ S , i ∣ j 或 j ∣ i \forall i,j \in S , \ \ i | j \ 或 \ j | i ∀i,j∈S, i∣j 或 j∣i,集合长度为 1 1 1 默认合法。
简要分析
不难发现,最长长度的集合长这样 l , l × 2 , l × 2 2 , . . . , l × 2 x l ,l\times 2,l\times 2^2 , ...,l \times2^x l,l×2,l×22,...,l×2x。
那么我们就可以处理出集合的最长长度。
接下来考虑仅 × 2 \times 2 ×2 时的集合数量。
显然,我们只需将 r r r 除以 x − 1 x - 1 x−1 次 2 2 2 就可以知道集合的初值所属于的最大区间,即求出了集合数量。
我们考虑到,可以将集合中的某一个 × 2 \times 2 ×2 替换成 × 3 \times3 ×3。
如果替换两个即等价于 × 9 \times 9 ×9,这时不妨使用 3 3 3 个 × 2 \times 2 ×2 即为 × 8 \times 8 ×8 得到更优答案。
对于计算存在一个 × 3 \times 3 ×3 的集合个数,我们如法炮制。
将 r r r 除以 x − 1 x-1 x−1 次 2 2 2 随后再除以 3 3 3 就可以知道集合的初值所属于的最大区间,即求出了集合数量。
时间复杂度 O ( T log r ) O(T\log r) O(Tlogr)。
代码实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 7;
const ll INF = 1e9 + 7, MOD = 998244353;
inline ll read() {
char cCc;
ll xXx = 0, wWw = 1;
while (cCc < '0' || cCc > '9')
(cCc == '-') && (wWw = -wWw), cCc = getchar();
while (cCc >= '0' && cCc <= '9')
xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
xXx *= wWw;
return xXx;
}
inline void write(ll xXx) {
if (xXx < 0)
putchar('-'), xXx = -xXx;
if (xXx > 9)
write(xXx / 10);
putchar(xXx % 10 + '0');
}
void solve() {
ll n = read(), m = read();
ll tmp = m, ans = 0, cnt = 0, lim;
if (n * 2 > m) {
cout << 1 << " " << m - n + 1 << '\n';
return;
}
while (tmp >= n) cnt++, tmp /= 2;
lim = cnt - 1;
tmp = m;
while (lim--) tmp /= 2;
ans = tmp - n + 1;
lim = cnt - 2;
tmp = m;
while (lim--) tmp /= 2;
ans += max(0ll, (tmp / 3 - n + 1) * (cnt - 1));
cout << cnt << ' ' << ans << endl;
}
signed main() {
// freopen("code.in","r",stdin);
// freopen("code.out","w",stdout);
ll T = read();
while (T--)solve();
return 0;
}
D. Maximum Subarray
题意描述
简要分析
观察到要求最大子段和,但发现需要对数组进行操作,于是考虑如何改编最大字段和。
这里我们改变使用分治(线段树)的方法求解。
关于如何使用分治(线段树)的方法求解最大子段和这里不再赘述。
观察到在建树时递归到最底端,也就是 l = = r l == r l==r 是,此时维护的最大字段和、以左端点为首元素的最大子段和,以右端点为尾元素的最大子段和均等于 a l a_l al 即 a r a_r ar。
我们可以在这里分类讨论,如果 k = 0 k = 0 k=0 那么只需将 a l − x a_l - x al−x 即 a r − x a_r - x ar−x 即可。
若 k ≠ 0 k \not= 0 k=0 要么将 a l + x a_l + x al+x 即 a r + x a_r + x ar+x 要么将 a l − x a_l - x al−x 即 a r − x a_r -x ar−x。
我们只需讲这两种情况均与另一节点合并至父节点中即可。
合并时仅需枚举所有配对情况取最大值即可。
又因为此题仅需一次建树一次查询所以可以省去许多冗余的函数,代码十分简洁。
时间复杂度为 O ( n k ) O(nk) O(nk)。
代码实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 7;
const ll INF = 1e18 + 7, MOD = 998244353;
inline ll read() {
char cCc;
ll xXx = 0, wWw = 1;
while (cCc < '0' || cCc > '9')
(cCc == '-') && (wWw = -wWw), cCc = getchar();
while (cCc >= '0' && cCc <= '9')
xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
xXx *= wWw;
return xXx;
}
inline void write(ll xXx) {
if (xXx < 0)
putchar('-'), xXx = -xXx;
if (xXx > 9)
write(xXx / 10);
putchar(xXx % 10 + '0');
}
struct node {
ll sum, lq, rq, qs;
node() {
sum = -INF, lq = -INF, rq = -INF, qs = -INF;
}
node(ll sum, ll lq, ll rq, ll qs) : sum(sum), lq(lq), rq(rq), qs(qs) {}
};
ll n, k, x, a[maxn];
vector<node> solve(ll l, ll r) {
if (l == r) {
ll s1 = max(0ll, a[r] + x);
ll s2 = max(0ll, a[r] - x);
if (k == 0) return {node(a[r] - x, s2, s2, s2)};
return {node(a[r] - x, s2, s2, s2), node(a[r] + x, s1, s1, s1)};
}
ll mid = (l + r) / 2;
auto vl = solve(l, mid), vr = solve(mid + 1, r);
ll len = min(k, r - l + 1) + 1;
vector<node> ans(len);
for (ll i = 0; i < vl.size(); i++) {
for (ll j = 0; j < vr.size() && i + j < len; j++) {
auto &l = vl[i], &r = vr[j], &u = ans[i + j];
u.sum = max(u.sum, l.sum + r.sum);
u.lq = max(u.lq, l.lq);
u.lq = max(u.lq, l.sum + r.lq);
u.rq = max(u.rq, r.rq);
u.rq = max(u.rq, l.rq + r.sum);
u.qs = max(u.qs, l.qs);
u.qs = max(u.qs, r.qs);
u.qs = max(u.qs, l.rq + r.lq);
}
}
return ans;
};
signed main() {
ll T = read();
while (T--) {
n = read(), k = read(), x = read();
for (ll i = 1; i <= n; i++) a[i] = read();
cout << solve(1, n)[k].qs << '\n';
}
}
E. Colored Subgraphs
题目大意
给你一棵树, n n n 个节点。你需要给每个节点染色,使得任意颜色相同的节点之间的路径上的点颜色也与前面两个点相同。
c n t i cnt_i cnti 表示颜色为 i i i 的节点数,找到 min c n t i \min cnt_i mincnti 的最大值。
简要分析
先不考虑,根不固定的情况,强制钦定 r = 1 r = 1 r=1,这里 r r r 指根节点。
我们发现答案通过二分可以转化成判定性问题,即能否将树划分成若干条自底向上目长度至少为 k k k 的链。
设 d e p u dep_u depu 表示,自底向上的到达 u u u 的最短长度, m i n x minx minx 表示么 x x x 的儿子中 d e p v dep_v depv 的最小值。
显然对于叶子有, d e p w = 1 dep_w=1 depw=1,而对于非时子节点,显然有转移 d e p w = m i n x + 1 dep_w=minx +1 depw=minx+1。
对于不合法的情况, u u u 存在两个儿子,他们的 d e p w dep_w depw 都要小于 k k k,因为 u u u 只能和一个儿子往上拼接,两个就不行了,这个记为情況 1 1 1。
最终只需要判断一下 d e p 1 dep_1 dep1, 是否不小于 k k k 即可,这个记为情况 2 2 2。
然后就是, r r r 不固定的情况,当然,假设说 r = 1 r = 1 r=1 可行那么我们就直接输出了。
如果情况 1 1 1 不合法,子树以外的点作为根是不可能的,因为这个子树的本身结构是不变的,所以我们就需要将 m i n x minx minx 对应的节点 v v v 变成根。
因为这种情况不合法一定是说明了这个点到 u u u 距离是小于 k k k 的,而肯定还有一个 v ’ v’ v’ 到 u u u 的距离是小于 v v v 的,所以我们本质上是把 v v v 到 v ’ v’ v’ 的路径涂成一种颜色。
如果情况 2 2 2 不合法,那么我们同理,也是找 m i n x minx minx 就行了。
总时间复杂度 O ( n ) O(n) O(n)。
代码实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const ll maxn = 2e5 + 7;
const ll INF = 1e18 + 7, MOD = 998244353;
inline ll read() {
char cCc;
ll xXx = 0, wWw = 1;
while (cCc < '0' || cCc > '9')
(cCc == '-') && (wWw = -wWw), cCc = getchar();
while (cCc >= '0' && cCc <= '9')
xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
xXx *= wWw;
return xXx;
}
inline void write(ll xXx) {
if (xXx < 0)
putchar('-'), xXx = -xXx;
if (xXx > 9)
write(xXx / 10);
putchar(xXx % 10 + '0');
}
ll n, tx, ty, dep[maxn], minnode[maxn];
vector<ll> G[maxn];
bool dfs(ll u, ll fa, ll k) {
ll x = 0, y = 0, miny = INF, minx = INF;
for (auto v : G[u]) {
if (v == fa)continue;
if (!dfs(v, u, k))return false;
if (dep[v] <= minx)y = x, miny = minx, x = minnode[v], minx = dep[v];
else if (dep[v] <= miny)y = minnode[v], miny = dep[v];
}
if (miny < k) {
tx = x, ty = y;
return false;
}
if (x == 0)dep[u] = 1, minnode[u] = u;
else dep[u] = minx + 1, minnode[u] = x;
if (!fa) {
if (dep[u] < k)tx = minnode[u], ty = 1;
return dep[u] >= k;
}
return true;
}
bool check(ll k) {
if (dfs(1, 0, k)) return true;
else if (dfs(tx, 0, k) || dfs(ty, 0, k))return true;
return false;
}
void solve() {
n = read();
for (ll i = 1; i < n; i++) {
ll u = read(), v = read();
G[u].push_back(v);
G[v].push_back(u);
}
ll l = 0, r = n, ans = 0;
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(mid))
ans = mid, l = mid + 1;
else r = mid - 1;
}
cout << ans << '\n';
for (ll i = 1; i <= n; i++)G[i].clear();
}
signed main() {
// freopen("code.in","r",stdin);
// freopen("code.out","w",stdout);
ll T = read();
while (T--)solve();
return 0;
}
总结
赛时慎用模版,听说有好多人使用同个模版,近似逐字符相同,然后就无了。
—— C l a u d e Z . \mathscr{ClaudeZ.} ClaudeZ.