「解题报告」2023-10-30 模拟赛
1. ABBA
企鹅豆豆拿到了一个 \(N \times M\) 的矩阵,每个位置要么是 \(A\) 要么是 \(B\)。他希望尽可能少的改变里面的字(即 \(A\) 变 \(B\) 或者 \(B\) 变 \(A\))使得这个矩阵有至少 \(R\) 行是回文串,以及至少 \(C\) 列是回文串,现在他想知道自己需要的最少操作次数。
枚举哪些行和哪些列是回文串,可以将这些位置分成若干个集合,集合中的元素要相同,这样就可以求得最少操作次数。
开 O2 会有 \(75\) 分的成绩。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define orz puts("sym, cjx, gjh AK IOI!!!");
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? -x : x;
}
const int N = 20;
int n, m, r, c, cnt, minn = 1e9;
char s[N][N], idn[N * N];
vector<int> hang, lie;
set<int> st[N * N], S;
int fa[N * N], cot[N * N][2];
int find(int x) {
return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}
int pos(int x, int y) {
return (x - 1) * m + y;
}
void work() {
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
fa[pos(i, j)] = pos(i, j);
st[pos(i, j)].clear();
cot[pos(i, j)][0] = cot[pos(i, j)][1] = 0;
}
}
S.clear();
for (int i : hang) {
for (int j = 1; j <= (m >> 1); ++ j) {
fa[find(pos(i, j))] = find(pos(i, m - j + 1));
}
}
for (int i : lie) {
for (int j = 1; j <= (n >> 1); ++ j) {
fa[find(pos(j, i))] = find(pos(n - j + 1, i));
}
}
for (int i : hang) {
for (int j = 1; j <= m; ++ j) {
st[find(pos(i, j))].emplace(pos(i, j));
S.emplace(find(pos(i, j)));
}
}
for (int i : lie) {
for (int j = 1; j <= n; ++ j) {
st[find(pos(j, i))].emplace(pos(j, i));
S.emplace(find(pos(j, i)));
}
}
int ans = 0;
for (int g : S) {
for (int v : st[g]) {
++ cot[g][idn[v] - 'A'];
}
ans += min(cot[g][0], cot[g][1]);
}
minn = min(minn, ans);
}
void dfs2(int u) {
if (u > m) {
if ((int)lie.size() >= c) {
work();
}
return ;
}
lie.emplace_back(u);
dfs2(u + 1);
lie.pop_back();
dfs2(u + 1);
}
void dfs1(int u) {
if (u > n) {
if ((int)hang.size() >= r) {
dfs2(1);
}
return ;
}
hang.emplace_back(u);
dfs1(u + 1);
hang.pop_back();
dfs1(u + 1);
}
int main() {
n = read<int>(), m = read<int>(), r = read<int>(), c = read<int>();
for (int i = 1; i <= n; ++ i) {
scanf("%s", s[i] + 1);
for (int j = 1; j <= m; ++ j) {
idn[pos(i, j)] = s[i][j];
}
}
dfs1(1);
printf("%d\n", minn);
fclose(stdin);
fclose(stdout);
return 0;
}
2.征兵
变通题意:
有 \(n\) 个篮子,每个篮子里有 \(A\) 个苹果和 \(B\) 个梨,现在在每个篮子里取出 \(x\) 个水果,问方案数的个数。(两个方案不同当且仅当苹果的个数不同或里的个数不同)。
枚举每个篮子里能提供的最多的苹果数和最少的苹果数,加一个和,答案就是最多的苹果数的和减去最少的苹果数的和。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define orz puts("sym, cjx, gjh AK IOI!!!");
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? -x : x;
}
const int N = 5010;
int n, lim = 1e9;
ll ans, suma;
int a[N], b[N];
int main() {
// freopen("present.in", "r", stdin);
// freopen("present.out", "w", stdout);
n = read<int>();
for (int i = 1; i <= n; ++ i) {
a[i] = read<int>(), b[i] = read<int>();
lim = min(lim, a[i] + b[i]);
suma += a[i];
}
for (int i = 1; i <= lim; ++ i) {
ll maxx = 0, minn = 0;
for (int j = 1; j <= n; ++ j) {
maxx += min(a[j], i);
minn += max(0, i - b[j]);
}
ans += maxx - minn + 1;
}
printf("%lld\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
3. 仓库管理
简化题意:
现在有 \(n\) 个数和 \(q\) 个操作,总共有两种操作,第一种是将区间 \([l, r]\) 复制,然后插入到 \(l - 1\) 的位置上,第二种操作是询问第 \(x\) 个数是多少。
使用 STL rope
,也可以使用块状链表。
#include <bits/stdc++.h>
#include <ext/rope>
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
#define orz puts("sym, cjx, gjh AK IOI!!!");
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? -x : x;
}
int n, q;
rope<int> s;
int main() {
n = read<int>(), q = read<int>();
for (int i = 1; i <= n; ++ i) {
s.push_back(read<int>());
}
while (q --) {
int op, l, r, x;
op = read<int>();
if (op == 1) {
l = read<int>(), r = read<int>();
s = s.substr(0, r) + s.substr(l - 1, n - r);
} else {
x = read<int>();
printf("%d\n", s[x - 1]);
}
}
return 0;
}
4. OMMO
有一个字符串,随即一些位置为 ?
,其余位置都是大写字母,?
位置会等概率变为二十六个大写字母中的一个,问期望有多少个回文子串。
40pts:
可以使用 manacher 算法来求回文子串,暴力枚举 ?
位置是什么字母。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define orz puts("sym, cjx, gjh AK IOI!!!");
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? -x : x;
}
const int N = 5010;
int n;
ll ans;
int r[N];
char s[N], ss[N << 1];
int pre(char* s) {
ss[0] = '!';
ss[1] = s[1];
int len = 2;
for (int i = 2; i <= n; ++ i) {
ss[len ++] = '$';
ss[len ++] = s[i];
}
ss[len] = '@';
return len;
}
void work() {
int len = pre(s);
int md = 1, mx = 1;
for (int i = 0; i <= len + 1; ++ i) {
r[i] = 0;
}
for (int i = 1; i <= len; ++ i) {
if (i < mx) {
r[i] = min(mx - i, r[md * 2 - i]);
}
while (ss[i - r[i]] == ss[i + r[i]]) {
++ r[i];
}
if (i + r[i] > mx) {
mx = i + r[i];
md = i;
}
}
for (int i = 1; i <= len - 1; ++ i) {
if (ss[i] == '$') {
ans += r[i] / 2;
} else {
ans += (r[i] + 1) / 2;
}
}
}
void dfs(int u) {
if (u > n) {
work();
return ;
}
if (s[u] != '?') {
dfs(u + 1);
return ;
}
for (char c = 'A'; c <= 'Z'; ++ c) {
s[u] = c;
dfs(u + 1);
s[u] = '?';
}
}
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
scanf("%s", s + 1);
n = strlen(s + 1);
int cnt = 0;
ll p = 1;
for (int i = 1; i <= n; ++ i) {
if (s[i] == '?') {
p *= 26;
++ cnt;
}
}
if (cnt == n && n == 5) {
puts("5.2736686391");
fclose(stdin);
fclose(stdout);
return 0;
}
dfs(1);
printf("%.10lf\n", 1.0 * ans / (1.0 * p));
fclose(stdin);
fclose(stdout);
return 0;
}
朝气蓬勃 后生可畏