Codeforces Global Round 12
A - Avoid Trygub
int main() {
IOS;
for (cin >> _; _; --_) {
string s; cin >> n >> s;
sort(all(s)); cout << s << '\n';
}
return 0;
}
B - Balls of Steel
枚举找就完事
PII a[N];
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n >> k;
rep (i, 1, n) cin >> a[i].fi >> a[i].se;
int ans = -1;
rep (i, 1, n) {
bool f = 0;
rep (j, 1, n) if (abs(a[i].fi - a[j].fi) + abs(a[i].se - a[j].se) > k) { f = 1; break; }
if (!f) { ans = 1; break; }
}
cout << ans << '\n';
}
return 0;
}
C1 & C2 - Errich-Tac-Toe
明显是考虑(i + j) % 3, 记 ++cnt[(i + j) % 3][s[i][j] == 'X']
最后无非选择 i, j(-1<i,j<3, i!=j) 使得 s[i][1] + s[j][0] <= k/3, 即使得每3个相邻的标记中存在两个不同的标记
一定存在这样的 i, j使得 <= k/3, 自己画个表就明白了
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n; memset(c, 0, sizeof c);
rep (i, 0, n - 1) {
cin >> s[i];
rep (j, 0, n - 1) if (s[i][j] != '.') ++c[(i + j) % 3][s[i][j] == 'X'], ++k;
}
rep (i, 0, 2) rep (j, 0, 2) if (i != j && c[i][1] + c[j][0] <= k / 3) {
rep (x, 0, n - 1) rep (y, 0, n - 1) if (s[x][y] != '.')
if ((x + y) % 3 == i) s[x][y] = 'O';
else if ((x + y) % 3 == j) s[x][y] = 'X';
k = 0;
}
rep (i, 0, n - 1) cout << s[i] << '\n';
}
return 0;
}
D - Rating Compression
md, 向获取 a[i] 能被选出来的范围 l~r, 用了线段树+倍增 t7
直接单调栈不好吗? 不过cf \(nlog^2n\) 常数稍大点竟然过不去, 离谱
nt a[N], b[N], l[N];
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n;
rep(i, 1, n) cin >> a[i], b[i] = 0;
stack<PII> q;
rep(i, 1, n) {
while (!q.empty() && q.top().fi >= a[i]) umax(b[q.top().fi], i - l[q.top().se]), q.pop();
if (q.empty()) l[i] = 1;
else l[i] = q.top().se + 1;
q.push({ a[i], i });
}
while (!q.empty()) umax(b[q.top().fi], n + 1 - l[q.top().se]), q.pop();
rep(i, 2, n) umin(b[i], b[i - 1]);
rep(i, 1, n) cout << (b[n - i + 1] >= i); cout << '\n';
}
return 0;
}
E - Capitalism
建图跑个多源最短路就好,
b = 1, d[u][v] = 1, d[v][u] = -1;
b = 0, d[u][v] = d[v][u] = 1;
分别跑出来以 i 为最少的资本, 的最短路
找到以谁为起点s, 差值最大就行
判 no, 就对于 d[s][u] == d[s][v] 为 no 就行
顺便用 d[i][i] 判个负环
int d[N][N];
int main() {
IOS; cin >> n >> m; vector<pair<PII, int>> e(m);
rep (i, 1, n) rep (j, 1, n) d[i][j] = i == j ? 0 : inf;
for (auto &i : e) {
cin >> i.fi.fi >> i.fi.se >> i.se;
d[i.fi.fi][i.fi.se] = 1;
d[i.fi.se][i.fi.fi] = i.se ? -1 : 1;
}
rep (k, 1, n) rep (i, 1, n) rep (j, 1, n) umin(d[i][j], d[i][k] + d[k][j]);
int mx = -1, w;
rep (i, 1, n) {
if (d[i][i] < 0) { cout << "NO"; return 0; }
for (auto &j : e) if (d[i][j.fi.fi] == d[i][j.fi.se]) { cout << "NO"; return 0; }
rep (j, 1, n) if (d[i][j] > mx) mx = d[i][j], w = i;
}
cout << "YES\n" << mx << '\n';
rep (i, 1, n) cout << d[w][i] << ' ';
return 0;
}
F - The Struggling Contestant
先把-1考虑了, 无非是某个数多余 (n + 1) / 2
然后是将序列分段, 记录每个数作为端点必须要出现的次数
比如 1 2 2 3 4 5, 对于2来说, a[2]于a[3]要隔开,并且这两个数要作为切割后的序列的端点(前端和后端)
对于 1, 5, 本来就是端点, 直接+1, 最后就是端点之间的相互拼接, 不过拼接的两个端点不能是相同的数,
你不能那端点 a[2] 和 a[3] 拼接, 但可以 a[2] 和 a[1]/a[6] 拼接, 既然都是端点了, 肯定贪心的先和其他端点拼接
最后如果存在 没有拼接的端点, 纪要使用那些单点了,
es 1 2 2 2 2 3 4, a[2] a[1] a[3] a[6] a[7] a[4] a[5] a[6]
当前剩下端点 a[4] 和 a[5], 只好使用 a[6] 去分割了 2 1 2 4 2 3 2
答案也就是 k + max(0, \(max_f[x]\) - k - 2), f[i] 表示 i 作为端点的数量, k是相邻端点的数量
简单来说 k, 是必须要所示的贡献, 两个相邻的相同的数, 拆入一个数, 最少贡献 +1, 1 2 2 -> 2 1 2 贡献为1
然后是相互插入 2 2 2 3 3 -> 2 3 2 3 2, 没贡献, 2 2 2 2 3 3 4 -> 2 3 2 3 2 4 2, 额外使用了单点 a[7], 贡献+1
1 2 2 2 3 3 4 4 4 5 -> 1 2 3 4 2 3 4 2 4 5, 无额外贡献, 说白了就是看那个最多的数作为端点其他数能不能将这个完全分割
为啥是 max(0, \(max_f[x]\) - k - 2) 要减个2? 你把那个最多的点作为整个序列的前后端点, 不挨着其他数
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n; VI a(n + 1), cnt(n + 1), f(n + 1); k = 0;
rep (i, 1, n) {
cin >> a[i]; ++cnt[a[i]];
if (i > 1 && a[i] == a[i - 1]) f[a[i]] += 2, ++k;
}
if (*max_element(all(cnt)) > (n + 1) / 2) { cout << "-1\n"; continue; }
++f[a[1]], ++f[a[n]];
cout << k + max(0, *max_element(all(f)) - k - 2) << '\n';
}
return 0;
}