CF Educational Round 49 题解
A - Palindromic Twist
题意:
给你一个包含\(n\)个小写字母的字符串\(S\)。保证\(n\)是偶数。对字符串\(S\)中的每一个位置\(i\),你需要执行以下操作:要么将其改为字母表中它的前一个字母,要么将其改为字母表中它的后一个字母。每个位置都应该恰好被执行一次该操作。问能不能在操作完了后把\(S\)变成一个回文串。
题解:
直接暴力枚举\(1\le i\le \dfrac{n}{2}\),若\(|S_i-S_{n-i+1}|\neq0\)或\(2\),则答案为NO
,否则YES
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
void solve() {
int n;
string s;
cin >> n >> s;
for (int i = 0; i < n / 2; ++i) {
char a = s[i], b = s[n - i - 1];
if (!(a == b || abs(a - b) == 2))
return void(puts("NO"));
}
puts("YES");
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int tests; cin >> tests;
while (tests--) solve();
return 0;
}
B - Numbers on the Chessboard
题意:
你有一个\(n\times n\)的棋盘,这个棋盘中的数字是由\(1\)至 \(n\times n\)的数组成的。这些数的填法满足:前面的\(⌈\dfrac{n×n}{2}⌉\)个数字,被按照从左到右、从上到下的次序依次写在那些横纵坐标相加为偶数的格子里。 剩下的\(n×n-⌈\dfrac{n×n}{2}⌉\)个数,也被按照如上顺序依次写在那些横纵坐标相加为奇数的格子里。你被给到了\(q\)个询问,第\(i\)个询问是形如\(x_i,y_i\)的。你要对应输出的是在坐标为\(x_i,y_i\)的格子里的数字(\(x_i\)为行,\(y_i\)为列),行和列的大小都是从\(1\)至\(n\)。
题解:
分类讨论,若\(x+y\mod2=0\),则这个数就是在区间\([1,⌈\dfrac{n×n}{2}⌉]\)里,然后看一下前面有多少行,计算一下前面行里有多少个这种数,然后再加一下第\(x\)行里面\((x,y)\)前的这种坐标的个数;若\(x+y\mod2=1\),则类似。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
ll n, q;
cin >> n >> q;
while (q--) {
ll x, y;
cin >> x >> y;
if ((x + y) % 2 == 0) {
y = (y + 1) / 2;
ll bef = x - 1;
ll ans = bef / 2 * n;
if (bef & 1) ans += (n + 1) / 2;
cout << ans + y << endl;
}
else {
y = (y + 1) / 2;
ll bef = x - 1;
ll ans = bef / 2 * n;
if (bef & 1) ans += n / 2;
cout << ans + y + (n * n + 1) / 2 << endl;
}
}
return 0;
}
C - Minimum Value Rectangle
题意:
你有\(n(1\le n\le 10^6)\)根给定长度的木棍。你的任务是从它们中选择恰好四根木棍使得其能形成一个矩形。木棍不能被切割。矩形的每一条边都只能使用一根木棍。一根木棍只能被使用一次。保证方案存在。若定义\(S\)表示矩形的面积,\(P\)表示矩形的周长,形成的矩形应使\(\dfrac{P^2}{S}\)尽可能小。保证数据有解,如有多种答案,任意输出其中一种。
题解:
先统计出来每一个长度的木棍有多少个。如果有一个长度的木棒个数\(\ge4\),那么就直接输出。把所有个数\(\ge2\)的木棍长度挑出来,放到一个数组里后拍个序。接着直接枚举相邻长度的木棒,计算出答案后统计最小值就行了。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
void solve() {
int n;
cin >> n;
map<int, int> a;
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
a[x]++;
}
vector<int> v;
for (auto &x: a) {
if (x.se >= 2) v.push_back(x.fi);
if (x.se >= 4) v.push_back(x.fi);
}
pii ans;
double ansf = 1e18;
for (int i = 0; i < (int)v.size() - 1; ++i) {
int a = v[i], b = v[i + 1];
double curans = 4. * (a + b) * (a + b) / (a * b);
if (ansf > curans) {
ansf = curans;
ans = pii(a, b);
}
}
cout << ans.fi << " " << ans.se << " " << ans.se << " " << ans.fi << endl;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int tests; cin >> tests;
while (tests--) solve();
return 0;
}
D - Mouse Hunt
题意:
有\(n(1\le n\le 2\times 10^5)\)个房间,每个房间放捕鼠夹有一个费用,老鼠每一秒从房间\(i\)跑到房间\(a_i\)(可以自环),在一开始老鼠会随机出现在每一个房间,求要抓住老鼠最小花费。
题解:
先强连通分量缩点,之后我们只需要在缩点后的图上找出每条链的末尾,在末尾节点所在的联通分量里面挑一个费用最小的房间放上捕鼠夹就行了。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
static const int Maxn = 200005;
int n;
int a[Maxn], c[Maxn];
vector<int> g[Maxn], ng[Maxn];
int dfn[Maxn], low[Maxn], instk[Maxn], scc[Maxn];
int index_, cnt, st[Maxn], top;
vector<int> has[Maxn];
void tarjan(int u) {
low[u] = dfn[u] = ++index_;
st[++top] = u, instk[u] = 1;
for (int &v: g[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instk[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
cnt++;
while (st[top] != u) {
scc[st[top]] = cnt;
has[cnt].push_back(st[top]);
instk[st[top--]] = 0;
}
scc[st[top]] = cnt;
has[cnt].push_back(st[top]);
instk[st[top--]] = 0;
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> c[i];
for (int i = 1; i <= n; ++i) {
cin >> a[i];
g[i].push_back(a[i]);
}
cnt = index_ = 0;
memset(dfn, 0, sizeof(dfn));
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) tarjan(i);
}
for (int i = 1; i <= n; ++i) {
if (scc[i] != scc[a[i]]) {
ng[scc[i]].push_back(scc[a[i]]);
}
}
int ans = 0;
for (int i = 1; i <= cnt; ++i) {
if (ng[i].empty()) {
int now = 1e9;
for (int &u: has[i]) {
now = min(now, c[u]);
}
ans += now;
}
}
cout << ans << endl;
return 0;
}
E - Inverse Coloring
题意:
您有一个由\(n\times n(1\le n\le 500)\)的正方形板。其中的每个图块的颜色为白色或黑色。如果一个正方形板符合一下条件:
- 对于第\(i(1\le i<n)\)行,第\(i\)行的第\(j(1 \le j \le n)\)个图块颜色与第\(i+1\)行的第\(j\)个图块颜色都相同,或者第\(i\)行的第\(j(1 \le j \le n)\)个图块颜色与第\(i+1\)行的第\(j\)个图块颜色都不同。
- 对于第\(i(1\le i<n)\)列,第\(i\)列的第\(j(1 \le j \le n)\)个图块颜色与第\(i+1\)列的第\(j\)个图块颜色都相同,或者第\(i\)列的第\(j(1 \le j \le n)\)个图块颜色与第\(i+1\)列的第\(j\)个图块颜色都不同。
那么我们称这个正方形版为漂亮着色。如果它是漂亮着色并且不存在有一个单色矩形内的图块数大于等于\(k\),我们就称其为完美着色。您的任务是计算给定大小的正方形板的完美着色方案数。请输出答案对\(998244353\)取模后的结果 。
题解:
先观察可得:只要确定了这个正方形板的第\(1\)行和第\(1\)列,就能确定整个图,此时最大的单色矩形应为第\(1\)行最长的单色条的长度\(\times\)第\(1\)列最长的单色条的长度。又因为第\(1\)行和第\(1\)列是互不干扰的,所以可以设\(dp_{i,j,k}\)表示在进行到第\(i\)个位置,结尾有连续\(j\)个方块颜色一样的,目前的最长单色条的长度为\(k\)的方案数。则状态转移方程为:
\(dp_{i+1,j+1,\max(j+1,k)}+=dp_{i,j,k}\)(第\(i+1\)位颜色和第\(i\)位一样)
\(dp_{i+1,1,k}+=dp_{i,j,k}\)(第\(i+1\)位颜色和第\(i\)位不一样)
直接写会是MLE的,所以可以滚动数组优化\(dp\),这样空间复杂度是\(O(n^2)\),时间复杂度是\(O(n^3)\)
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int mod = 998244353;
inline int add(int x, int y) { return (x += y) >= mod ? x - mod : x; }
inline void inc(int &x, int y) { (x += y) >= mod && (x -= mod); }
inline int mul(int x, int y) { return 1LL * x * y - 1LL * x * y / mod * mod; }
int n, K;
int dp[2][505][505];
int sum[505];
signed main() {
register int i, j, k;
cin >> n >> K;
dp[1][1][1] = 1;
for (i = 1; i < n; ++i) {
int cur = i & 1, nxt = i & 1 ^ 1;
memset(dp[nxt], 0, sizeof(dp[nxt]));
for (j = 1; j <= i; ++j) {
for (k = j; k <= i; ++k) {
if (!dp[cur][j][k]) continue;
inc(dp[nxt][j + 1][max(j + 1, k)], dp[cur][j][k]);
inc(dp[nxt][1][k], dp[cur][j][k]);
}
}
}
for (j = 1; j <= n; ++j)
for (k = j; k <= n; ++k)
inc(sum[k], dp[n & 1][j][k]);
int ans = 0;
for (i = 1; i <= n; ++i)
for (j = 1; j <= n; ++j)
if (i * j < K)
inc(ans, mul(sum[i], sum[j]));
inc(ans, ans);
cout << ans << endl;
return 0;
}
F - Session in BSU
题意:
有\(n(1\le n\le 10^6)\)场考试,每场考试只能在\(a_i\)或\(b_i\)这两个时间参加,一个时间不能参加多场比赛,问最早什么时候参加完所有比赛,如果不能参加完,输出-1
。
题解:
考虑把题目条件转化成一个图,先把所有时间离散化,然后把时间当成点,每场比赛的两个时间\(a_i,b_i\)之间建一条无向边。那么现在就是要把所有边都配一个点,问最少要用什么编号的点。
这个图是不一定联通的。对于每一个联通块\(S\),设\(S\)有\(v\)个点,\(e\)条边。若\(v=e+1\),则该联通块是一棵树,不管去掉哪一个点都能实现边和点的匹配,所以只要找出这个联通块里最大的编号的点;若\(v=e\),则该联通块是一个基环树,每一个边都正好能和一个点匹配,这个时候这个联通块里面的所有点都有用;若\(v<e\),则不能实现每一条边都配一个点,所以此时答案为-1
。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int Maxn = 2000005;
int n, m, cnt_v, cnt_e, mx;
int a[Maxn], b[Maxn];
vector<int> times;
vector<int> g[Maxn];
bool vis[Maxn];
bool choose[Maxn];
void dfs(int u, int fa) {
if (vis[u]) return;
cnt_v++;
mx = max(mx, u);
vis[u] = true;
choose[u] = true;
for (int &v: g[u]) {
cnt_e++;
if (v == fa) continue;
dfs(v, u);
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> b[i];
times.push_back(a[i]);
times.push_back(b[i]);
}
sort(times.begin(), times.end());
times.erase(unique(times.begin(), times.end()), times.end());
m = (int)times.size();
if (m < n) return puts("-1") & 0;
for (int i = 1; i <= n; ++i) {
a[i] = lower_bound(times.begin(), times.end(), a[i]) - times.begin();
b[i] = lower_bound(times.begin(), times.end(), b[i]) - times.begin();
g[a[i]].push_back(b[i]);
g[b[i]].push_back(a[i]);
}
for (int i = 0; i < m; ++i) {
if (!vis[i]) {
cnt_v = 0, cnt_e = 0;
mx = i;
dfs(i, -1);
cnt_e >>= 1;
if (cnt_v == cnt_e + 1) {
choose[mx] = false;
}
else if (cnt_v < cnt_e) {
puts("-1");
return 0;
}
}
}
for (int i = m - 1; i >= 0; --i) {
if (choose[i]) {
cout << times[i] << endl;
return 0;
}
}
return 0;
}