「解题报告」2023-10-30 模拟赛

发布时间 2023-10-30 22:58:52作者: yi_fan0305

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;
}