2025年四川省赛

https://codeforces.com/gym/105949

H. Hututu

题意:在网格中,从 (x, y) 移动到 (X, Y) 。移动操作为:\((x\pm1,y\pm1),(x\pm2,y\pm2),(x\pm1,y\pm2),(x\pm2,y\pm1)\),求最小步数。

题解:尽可能走最远,也就是 x 轴和 y 轴走 2 步。\(c=min(\lceil\frac{abs(X-x)}{2}\rceil,\lceil\frac{abs(Y-y)}{2}\rceil)\) ,走 c 步之后,有一个轴是已经到达了,继续走另外一个轴。

但是如果起始就有一个轴达到了,另外一个轴没有达到,那么步数至少为 2. 因为没有方法可以垂直移动或者水平移动。

void slove() {
	int a, b, x, y; cin >> a >> b >> x >> y;
	if (a == x && b == y) {
		cout << 0 << endl;
		return;
	}
	int res1 = (abs(a - x) + 1) / 2, res2 = (abs(b - y) + 1) / 2;
	int c = min(res1, res2);
	int X = max(0, abs(a - x) - c * 2), Y = max(0, abs(b - y) - c * 2);
	c += (X + 1) / 2 + (Y + 1) / 2;
	if (abs(a - x) == 0 || abs(b - y) == 0) {
		c = max(2, c);	
	}
	cout << c << endl;
}

I. Essentially Different Suffixe

题意:给定 n 个字符串,求有多少个不同的后缀。

题解:每个字符串的后缀记录在 trie 中。

struct Node {
	Node* son[26];
	int cnt;
	Node() {
		for (int i = 0; i < 26; i++) {
			son[i] = nullptr;
		}
		cnt = 0;
	}
};
void slove() {
	int n; cin >> n;
	vector<string> a(n + 5);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	int m = a[0].size();
	int res = 0;
	Node* root = new Node();
	for (int i = 0; i < n; i++) {
		string s = a[i];
		Node* cur = root;
		int sum = 0;  // 重复的后缀
		for (int j = s.size() - 1; j >= 0; j--) {
			res ++;  // 所有后缀
			int u = s[j] - 'a';
			if (cur -> son[u] == nullptr) {
				cur -> son[u] = new Node();
			} 
			cur = cur -> son[u];
			cur -> cnt++;
			if (cur -> cnt >= 2) {
				sum++;
			}
		}
		res -= sum;
	}	
	cout << res << endl;
}

F. Inversion Pairs

题意:给定一个字符串,只有 '0', '1', '?' ,其中 '?' 可以变成 '0' 或 '1',求逆序队的最大数量。

题解:对于所有的 '?' 一定存在一个分界点,使得前一部分填 '1',后一部分填 '0'。如果前一部分有一个位置为 0,将它放在所有的 1 后面,一定更优。
考虑先将所有的 '?' 视为 '0',然后从左往右依次变为 '1'。维护最大值即可。

void slove() {
	int n; cin >> n;
	string s; cin >> s;
	s = "?" + s;
	int pre1 = 0;
	LL ans = 0, res = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] == '1') pre1++;
		else {
			res += pre1;
		}
	}
	ans = res;
	int aft0 = n - pre1;
	pre1 = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] == '1') {
			pre1++;
		} else if (s[i] == '0') {
			aft0--;
		} else {
			aft0--;
			res -= pre1;
			res += aft0;
			pre1++;
			ans = max(res, ans);
		}
	}
	cout << ans << endl;
}

J. Sichuan Provincial Contest

题意:给定一棵树,书上的节点为对应位置的字符串的字符。计算所有简单路径恰好包含 "SCCPC" 的数量。

题解:树形DP,f(u, [i = 0~4]) 表示 u 的子树中,包含目标字符串的前缀 [0, i] 的方案数。g(u, [i = 0~4]) 表示 u 的子树中,包含目标字符串的后缀 [i, 4] 的方案数。

方案计算分为两部分:

  1. 当前这个节点的子树就存在目标字符串:f(u, 4) + g(u, 0)。
  2. 可以由两个链拼接而成目标字符串,中间节点进行拼接:f(v, i - 1) * (u 的子数组中,后缀为 [i + 1, 4] 的方案数之和)。后一部分可以对 u 的子树后缀方案数求和,然后减去该链即可。
const int N = 1e6 + 5;
vector<int> g[N];
string s;
int n;
LL res;
LL pre[N][5], aft[N][5];
string t = "SCCPC";
void dfs(int u, int fa) {
	if (s[u] == t[0]) {
		pre[u][0] = 1;
	}
	if (s[u] == t[4]) {
		aft[u][4] = 1;
	}
	vector<LL> sum(5);  // u 的子树后缀方案数之和
	for (int v : g[u]) {
		if (v == fa) continue;
		dfs(v, u);
		
		for (int i = 0; i <= 4; i++) {
			sum[i] += aft[v][i];
			if (s[u] == t[i]) {
				if (i - 1 >= 0) {
					pre[u][i] += pre[v][i - 1];
				}
				if (i + 1 <= 4) {
					aft[u][i] += aft[v][i + 1];
				}
			}
		}
	}
	
	res += pre[u][4] + aft[u][0];
	for (int v : g[u]) {
		if (v == fa) continue;
		for (int i= 1; i <= 3; i++) {
			if (s[u] == t[i]) {
				res += pre[v][i - 1] * (sum[i + 1] - aft[v][i + 1]);
			}
		}
	}
}
void slove() {
	res = 0;
	cin >> n;
	cin >> s; s = "?" + s;
	for (int i = 1; i <= n; i++) {
		g[i].clear();
		for (int j = 0; j <= 4; j++) {
			pre[i][j] = aft[i][j] = 0;
		}
	}
	for (int i = 1; i <= n - 1; i++) {
		int u, v; cin >> u >> v;
		g[u].emplace_back(v);
		g[v].emplace_back(u);
	}	
	dfs(1, -1);
	cout << res << endl;
}

K. Point Divide and Conquer

题意:给定一颗树和对于顶点的排列,按照排列依次将树中的顶点删除,然后添加到新的树中,对于原树的若干个连通块,依然按照排列顺序将点删除,并且添加到新的树的节点下。求新树中每个节点的父节点编号。

题解:反向遍历排列,对于当前点 u,遍历所有邻居 v,如果 v 所在排列在 u 之后,并且不是一个连通块中,就将 v 所在连通块根的父亲设为 u,并且合并连通块,将 v 所在连通块挂在 u 下。

const int N = 1e5 + 5;
int a[N], p[N], pos[N];
vector<int> g[N];
int n;
int find(int x) {
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}
void slove() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		g[i].clear();
		p[i] = i;
	}
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		pos[a[i]] = i;
	}
	for (int i = 1; i <= n - 1; i++) {
		int u, v; cin >> u >> v;
		g[u].emplace_back(v);
		g[v].emplace_back(u);
	}
	vector<int> res(n + 5);
	for (int i = n; i >= 1; i--) {
		int u = a[i];
		for (int v : g[u]) {
			int pa = find(u), pb = find(v);
			if (pos[v] > i && pa != pb) {
				res[find(v)] = u;
				p[pb] = u;
			}
		}
	}	
	for (int i = 1; i <= n; i++) {
		cout << res[i] << " ";
	}
	cout << endl;
}
posted @ 2025-10-09 15:51  yairr  阅读(22)  评论(0)    收藏  举报