Codeforces Round 635 (Div. 2) 解题报告

对应场次为 CF1337(Div1=1336)

雀魂场


A. Ichihime and Triangle

image

题目大意

给定 \(a, b, c, d(1 \le a \le b \le c \le d \le 10^9)\) 输出一个合法构造 \(x, y, z\) 满足:

  • \(a \le x \le b\)
  • \(b \le y \le c\)
  • \(c \le z \le d\)
  • \(x, y, z\) 为三边可以构成一个三角形

数据保证有解

Solution

保证有解就非常好办
不难发现 \(x \le y \le z\) 根据两边之和大于第三边 只需要让 \(x + y > z\) 即可
那么我们让 \(x, y\) 取最大值 \(z\) 取最小值即可
体现在代码上就是输出 \(b, c, c\)

点击查看代码
/*
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9') 
		xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
	return xr * F;
}

void write(ll x) {
	char ws[51];
	int wt = 0;
	if (x < 0) putchar('-'), x = -x;
	do {
		ws[++wt] = x % 10 + '0';
		x /= 10;
	} while (x);
	for (int i = wt; i; --i) putchar(ws[i]);
}

namespace steven24 {

int T;
int a, b, c, d;

void main() {
	T = read();
	while (T--) {
		a = read(), b = read(), c = read(), d = read();
		write(b), putchar(' '), write(c), putchar(' '), write(c), putchar('\n');
	}
}

}

int main() {
	steven24::main();
	return 0;
}

B. Kana and Dragon Quest game

stereotype 刻板印象
gameholic 游戏狂
absorption 吸收,同化

image

题目大意

你要打败一条血量为 \(x(1 \le x \le 10^5)\) 的巨龙 你有两个技能可供选择
技能一:设当前血量为 \(h\) 将其变成 \(\left\lfloor\dfrac{h}{2}\right\rfloor + 10\)
技能二:设当前血量为 \(h\) 将其变成 \(h - 10\)
你可以最多使用技能一 \(n\) 次 技能二 \(m\)\((1 \le n, m \le 30)\)
求是否能让巨龙的血量 \(\le 0\)

Solution

首先发现技能一的伤害与当前血量挂钩 当前血量越高伤害越高
所以我们打完技能一再打技能二一定是更优的
注意到技能一可能会使血量增多 这显然不是我们想要的
所以我们循环 \(n\) 次技能一 如果打完血量增多就不打了
然后把 \(m\) 次技能二打满 看剩余血量是否 \(\le 0\) 即可

时间复杂度 \(\text{O}(T \times n)\)

点击查看代码
/*
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9') 
		xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
	return xr * F;
}

void write(ll x) {
	char ws[51];
	int wt = 0;
	if (x < 0) putchar('-'), x = -x;
	do {
		ws[++wt] = x % 10 + '0';
		x /= 10;
	} while (x);
	for (int i = wt; i; --i) putchar(ws[i]);
}

namespace steven24 {

int T;
int hp, n, m;

void main() {
	T = read();
	while (T--) {
		hp = read(), n = read(), m = read();
		while (n--) {
			if (hp / 2 + 10 <= hp) hp = hp / 2 + 10;
			else break;
		}
		hp -= 10 * m;
		if (hp <= 0) puts("YES");
		else puts("NO");
	}
}

}

int main() {
	steven24::main();
	return 0;
}

C. Linova and Kingdom

envoy 使者

image

终于没有 div2 卡 C 了 泪目

题目大意

给定一个 \(n(2 \le n \le 2 \times 10^5)\) 个点的树 指定1为根节点
给定一个 \(k(1 \le k < n)\) 你需要选择这棵树上的 \(k\) 个点作为特殊节点
定义一个特殊节点的贡献为到根节点路径上的非特殊节点数量
最大化所有特殊节点的贡献和

Solution

不难发现 对于一条链而言 选择最底下那个点作为特殊点一定是最优的
我们考虑选择一个非叶节点对答案的影响
首先会让答案增加自己的 \(dep\)
其次会让自己子树内的所有特殊点都减去1的贡献

这时候感性考虑一下 发现最优方案一定是从底下开始一层一层铺的 即选择这个点时一定满足它子树内所有点都已被选择
否则把不选这个点 转去选它子树内任意一个没被选的点 一定会更优
所以实际选择每个点的贡献就是 \(dep_i - siz_i + 1\)
直接 dfs 然后排序选前 \(k\) 个即可

点击查看代码
/*
跑dfs维护出siz和dep
按dep-siz+1排序 
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9') 
		xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
	return xr * F;
}

void write(ll x) {
	char ws[51];
	int wt = 0;
	if (x < 0) putchar('-'), x = -x;
	do {
		ws[++wt] = x % 10 + '0';
		x /= 10;
	} while (x);
	for (int i = wt; i; --i) putchar(ws[i]);
}

namespace steven24 {

const int N = 2e5 + 0721;
int head[N], nxt[N << 1], to[N << 1], cnt;
int siz[N], dep[N];
int a[N];
int n, k;
ll ans;

inline void add_edge(int x, int y) {
	to[++cnt] = y;
	nxt[cnt] = head[x];
	head[x] = cnt;
}

inline bool cmp(int x, int y) {
	return x > y;
}

void dfs(int x, int fa) {
	siz[x] = 1;
	dep[x] = dep[fa] + 1;
	for (int i = head[x]; i; i = nxt[i]) {
		int y = to[i];
		if (y == fa) continue;
		dfs(y, x);
		siz[x] += siz[y];
	}
}

void main() {
	n = read(), k = read();
	for (int i = 1; i < n; ++i) {
		int x = read(), y = read();
		add_edge(x, y);
		add_edge(y, x);
	}
	dep[0] = -1;
	dfs(1, 0);
	for (int i = 1; i <= n; ++i) a[i] = dep[i] - siz[i] + 1;
	sort(a + 1, a + 1 + n, cmp);
	for (int i = 1; i <= k; ++i) ans += a[i];
	write(ans), putchar('\n');
}

}

int main() {
	steven24::main();
	return 0;
}

D. Xenia and Colorful Gems

noble 出身高贵的人
harshness 严肃
gem 宝石

image

题目大意

给你三个长为 \(n_r, n_g, n_b(1 \le n_r, n_g, n_b \le 10^5)\) 的序列 \(r, g, b\)
\((r_i - g_j) ^ 2 + (g_j - b_k) ^ 2 + (b_k - r_i) ^ 2\) 的最小值

Solution

首先排序肯定是没有问题的
三元组 考虑枚举中间的元素
不妨设 \(r_i \le g_j \le b_k\) 那就枚举 \(g_j\) 然后查询 \(r\) 中比 \(g_j\) 小的最大元素和 \(b\) 中比 \(g_j\) 大的最小元素 统计答案
写个锤子二分 不如双指针
由于 \(r_i, g_j, b_k\) 的大小关系不一定是上面那个 所以互换位置做六次即可

时间复杂度 \(\text{O}(n \log n)\) 主要是排序

点击查看代码
/*
*/
#include <bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9') 
		xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
	return xr * F;
}

void write(ll x) {
	char ws[51];
	int wt = 0;
	if (x < 0) putchar('-'), x = -x;
	do {
		ws[++wt] = x % 10 + '0';
		x /= 10;
	} while (x);
	for (int i = wt; i; --i) putchar(ws[i]);
}

namespace steven24 {

const int N = 1e5 + 0721;
const ll inf = LLONG_MAX;
int n1, n2, n3;
int a[N], b[N], c[N];
int T;
ll ans;

inline ll calc(int x, int y, int z) {
	return 1ll * (x - y) * (x - y) + 1ll * (y - z) * (y - z) + 1ll * (z - x) * (z - x);
}

void main() {
	T = read();
	while (T--) {
		n1 = read(), n2 = read(), n3 = read();
		for (int i = 1; i <= n1; ++i) a[i] = read();
		for (int i = 1; i <= n2; ++i) b[i] = read();
		for (int i = 1; i <= n3; ++i) c[i] = read();
		sort(a + 1, a + n1 + 1);
		sort(b + 1, b + n2 + 1);
		sort(c + 1, c + n3 + 1);
		ans = inf;
		
		int p1 = 0, p2 = 0, p3 = 0;
		for (int i = 1; i <= n1; ++i) {
			while (a[i] >= b[p2 + 1] && p2 + 1 <= n2) ++p2;
			while (a[i] > c[p3] && p3 + 1 <= n3) ++p3;
			if (p2 && p3) ans = min(ans, calc(a[i], b[p2], c[p3]));
		}
		p2 = p3 = 0;
		for (int i = 1; i <= n1; ++i) {
			while (a[i] > b[p2] && p2 + 1 <= n2) ++p2;
			while (a[i] >= c[p3 + 1] && p3 + 1 <= n3) ++p3;
			if (p2 && p3) ans = min(ans, calc(a[i], b[p2], c[p3]));
		}
		
		p1 = p3 = 0;
		for (int i = 1; i <= n2; ++i) {
			while (b[i] >= a[p1 + 1] && p1 + 1 <= n1) ++p1;
			while (b[i] > c[p3] && p3 + 1 <= n3) ++p3;
			if (p1 && p3) ans = min(ans, calc(b[i], a[p1], c[p3]));
		}
		p1 = p3 = 0;
		for (int i = 1; i <= n2; ++i) {
			while (b[i] > a[p1] && p1 + 1 <= n1) ++p1;
			while (b[i] >= c[p3 + 1] && p3 + 1 <= n3) ++p3;
			if (p1 && p3) ans = min(ans, calc(b[i], a[p1], c[p3]));
		}
		
		p1 = p2 = 0;
		for (int i = 1; i <= n3; ++i) {
			while (c[i] >= a[p1 + 1] && p1 + 1 <= n1) ++p1;
			while (c[i] > b[p2] && p2 + 1 <= n2) ++p2;
			if (p1 && p2) ans = min(ans, calc(c[i], a[p1], b[p2]));
		}
		p1 = p2 = 0;
		for (int i = 1; i <= n3; ++i) {
			while (c[i] > a[p1] && p1 + 1 <= n1) ++p1;
			while (c[i] >= b[p2 + 1] && p2 + 1 <= n2) ++p2;
			if (p1 && p2) ans = min(ans, calc(c[i], a[p1], b[p2]));
		}
		
		write(ans), putchar('\n');
	}
}

}

signed main() {
	steven24::main();
	return 0;
}

E. Kaavi and Magic Spell

inevitable 不可避免的
divination 占卜

image

题目大意

给定两个字符串 \(S(1 \le |S| \le 3000)\)\(T(1 \le |T| \le |S|)\)
你有一个空的双端队列 \(A\)
每次取出 \(S\) 的第一个元素 推入 \(A\) 的队头或队尾
问有多少种方式使 \(A\) 中当前的字符串有 \(T\) 作为前缀(包括 \(S\) 中还有元素时)

Solution

很巧妙的一道题
推入队头或队尾的操作不太好处理 但是因为最后一定要存在一个 \(T\) 作为前缀 所以我们可以考虑把这个东西映射到 \(T\)
具体地 我们设 \(f_{l, r}\) 表示把 \(S\) 的前 \(r - l + 1\) 个元素都推入队 它们构成了 \(t\)\(\left[l, r\right]\) 这段区间的方案数
这样就有转移:

  • \(f_{l, r} += f_{l + 1, r} (s_{r - l + 1} = t_l)\)
  • \(f_{l, r} += f_{l, r - 1} (s_{r - l + 1} = t_r)\)
  • \(f_{i, i} = 2 (t_i = s_1)\)

但是我们发现 这样转移只能处理到恰好拼出来 \(T\) 的情况 那么如何处理前缀问题呢
我们扩大化定义 用 \(f_{l, r}\) 映射到 \(t\) 串上 \(\left[l, r\right]\)\(\left[l, lent\right]\) 这两段区间的交集
这样我们能作为答案串不在 \(t\) 的那部分 就会通过这个定义直接“甩”在后面
进一步有这些转移:

  • \(f_{l, r} += f_{l + 1, r} (l > lent)\)
  • \(f_{l, r} += f_{l, r - 1} (r > lent)\)
  • \(f_{i, i} = 2 (lent < i \le lens)\)

那么最后答案就为 \(\sum\limits_{i = lent}^{lens} f_{1, i}\)

点击查看代码
/*
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline int read() {
	int xr = 0, F = 1;
	char cr;
	while (cr = getchar(), cr < '0' || cr > '9') if (cr == '-') F = -1;
	while (cr >= '0' && cr <= '9') 
		xr = (xr << 3) + (xr << 1) + (cr ^ 48), cr = getchar();
	return xr * F;
}

void write(ll x) {
	char ws[51];
	int wt = 0;
	if (x < 0) putchar('-'), x = -x;
	do {
		ws[++wt] = x % 10 + '0';
		x /= 10;
	} while (x);
	for (int i = wt; i; --i) putchar(ws[i]);
}

namespace steven24 {

const int N = 0x0d00;
const int mod = 998244353;
char s[N], t[N];
ll f[N][N];
int slen, tlen;

void main() {
	scanf("%s%s", s + 1, t + 1);
	slen = strlen(s + 1), tlen = strlen(t + 1);
	for (int i = 1; i <= tlen; ++i) if (t[i] == s[1]) f[i][i] = 2;
	for (int i = tlen + 1; i <= slen; ++i) f[i][i] = 2;
	for (int len = 2; len <= slen; ++len) {
		for (int l = 1; l + len - 1 <= slen; ++l) {
			int r = l + len - 1;
			if (t[l] == s[len] || l > tlen) f[l][r] = (f[l][r] + f[l + 1][r]) % mod;
			if (t[r] == s[len] || r > tlen) f[l][r] = (f[l][r] + f[l][r - 1]) % mod;
		}
	}
	ll ans = 0;
	for (int i = tlen; i <= slen; ++i) ans = (ans + f[1][i]) % mod;
	write(ans), putchar('\n');
}

}

int main() {
	steven24::main();
	return 0;
}
posted @ 2023-10-27 07:29  Steven24  阅读(514)  评论(1)    收藏  举报