Educational Codeforces Round 53 (Rated for Div. 2)

比赛链接

Educational Codeforces Round 53 (Rated for Div. 2)

A. Diverse Substring

题面

给定一个字符串 S S S,求出一个子串满足子串中出现的所有字母出现次数不超过这个子串长度的一半(下取整),如果不存在输出NO。

解法
  • 显然,只要不是所有字符都相同,那么这个串一定存在这样的子串。我们只要找到一个长度为2且两个字符不相同的子串就可以了。
  • 时间复杂度: O ( n ) O(n) O(n)
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int main() {
	int l; cin >> l;
	string st; cin >> st;
	for (int i = 0; i < l - 1; i++)
		if (st[i] != st[i + 1]) return cout << "YES\n" << st[i] << st[i + 1] << "\n", 0;
	cout << "NO\n";
	return 0;
}

B. Vasya and Books

题面

给定一个入栈序列,然后每一次按照顺序询问从栈顶取出 x x x时至少要往栈中放入多少个数。保证询问合法。

解法
  • 因为保证询问合法且按照顺序,那么我们直接按照题意模拟即可
  • 时间复杂度: O ( n ) O(n) O(n)
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int a[200010], used[200010];
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1, l = 0; i <= n; i++) {
		int x, s = 0; read(x);
		while (l < n && a[l] != x && !used[x]) l++, s++, used[a[l]] = 1;
		cout << s << ' ';
	}
	cout << "\n";
	return 0;
}

C. Vasya and Robot

题面

给定一个长度为 n n n的操作序列,可以上下左右移动。可以修改一些操作,修改的代价为修改的最大位置-最小位置+1。问从 ( 0 , 0 ) (0,0) (0,0)走到一个定点 ( x , y ) (x,y) (x,y)的最小代价,如果无解输出 − 1 -1 1
n ≤ 2 × 1 0 5 n≤2×10^5 n2×105

解法
  • 可以先将无解的情况去除:1. ( 0 , 0 ) (0,0) (0,0) ( x , y ) (x,y) (x,y)的曼哈顿距离和 n n n奇偶不同 2. n n n小于 ( 0 , 0 ) (0,0) (0,0) ( x , y ) (x,y) (x,y)的曼哈顿距离。然后判断答案是否可以为0。
  • 那么现在就一定需要修改。可以发现,这个答案其实是满足单调性的,所以我们可以考虑二分答案,假设为 m i d mid mid。那么问题就转化成如何检查答案合法性。
  • 考虑枚举左端点 l l l,那么可以进行修改的区间就变成 [ l , l + m i d − 1 ] [l,l+mid-1] [l,l+mid1],我们可以计算出除去这个区间其他操作带来的影响,然后判断一下当前坐标到 ( x , y ) (x,y) (x,y)的曼哈顿距离是否不超过 m i d mid mid即可。
  • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
代码
#include <bits/stdc++.h>
#define N 200010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int n, sx, sy; char st[N]; map <char, char> rev;
void Move(char c, int &x, int &y) {
	if (c == 'U') y++; if (c == 'D') y--;
	if (c == 'L') x--; if (c == 'R') x++;
}
bool check(int mid) {
	int ax = 0, ay = 0, bx = 0, by = 0;
	for (int i = mid + 1; i <= n; i++) Move(st[i], bx, by);
	for (int i = 1; i <= n - mid + 1; i++) {
		int x = ax + bx, y = ay + by;
		if (abs(sx - x) + abs(sy - y) <= mid) return true;
		Move(st[i], ax, ay), Move(rev[st[i + mid]], bx, by);
	}
	return false;
}
int main() {
	cin >> n >> st + 1 >> sx >> sy;
	if (abs(sx) + abs(sy) > n || (abs(sx) + abs(sy)) % 2 != n % 2) return cout << "-1\n", 0;
	int x = 0, y = 0;
	for (int i = 1; i <= n; i++) Move(st[i], x, y);
	if (x == sx && y == sy) return cout << "0\n", 0;
	rev['U'] = 'D', rev['D'] = 'U', rev['L'] = 'R', rev['R'] = 'L';
	int l = 1, r = n, ans = -1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) ans = mid, r = mid - 1;
			else l = mid + 1;
	}
	cout << ans << "\n";
	return 0;
}

D. Berland Fair

题面

有一个长度为 n n n的环,每一个点上有一个权值 a [ i ] a[i] a[i]。你一开始有一个权值 T T T,从1号点出发,沿着环进行如下操作:如果当前的 T T T不小于 a [ i ] a[i] a[i],那么 T − a [ i ] , a n s + 1 T-a[i],ans+1 Ta[i],ans+1,否则 T T T不变。问最后的 a n s ans ans
n ≤ 2 × 1 0 5 , T ≤ 1 0 18 n≤2×10^5,T≤10^{18} n2×105,T1018

解法
  • 假设从 1 1 1号点出发回到 1 1 1号点为一次更新,那么显然相同的更新情况一定是一段连续的区间组成的。
  • 那么我们就可以暴力做这个过程。每一次从 1 1 1 n n n计算会选择哪些点以及 T T T的情况,然后可以 O ( 1 ) O(1) O(1)计算不断这样做直到不能做为止的时候 T T T的大小和 a n s ans ans的情况。然后重复上述过程即可。
  • 可以发现,因为有取模操作,所以最坏情况下 T T T每一次缩小一半。
  • 时间复杂度: O ( n log ⁡ T ) O(n\log T) O(nlogT)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
ll a[200010];
int main() {
	int n; ll T, ans = 0; read(n), read(T);
    for (int i = 1; i <= n; i++) read(a[i]);
    while (true) {
        ll sum = 0, cnt = 0;
        for (int i = 1; i <= n; i++) 
			if (T >= a[i]) T -= a[i], cnt++, sum += a[i];
        if (sum > 0) cnt *= (T / sum) + 1, T %= sum, ans += cnt;
        	else break;
    }
    cout << ans << "\n";
	return 0;
}

E. Segment Sum

题面

给定三个数 l , r , k l,r,k l,r,k,问区间 [ l , r ] [l,r] [l,r]中有所有出现不超过 k k k种数字的数的和。
l , r ≤ 1 0 18 l,r≤10^{18} l,r1018

解法
  • 显然可以数位dp
  • f [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] f[i][j][0/1][0/1] f[i][j][0/1][0/1]表示当前做到数的第 i i i位,选择数字的集合为 j j j,是否有前导零和前 i i i位是否完全和原数相同的答案。这里的答案返回的是一个pair,记录的是满足这些条件的数的个数以及这些数的和。
  • 转移比较简单,假设当前枚举到的位置填 x x x,有 s s s个数满足要求,那么这一位为 x x x对答案的贡献就可以表示成 1 0 i − 1 s x 10^{i-1}sx 10i1sx
  • 时间复杂度: O ( 2 10 × 18 × 10 × 4 ) O(2^{10}×18×10×4) O(210×18×10×4)
代码
#include <bits/stdc++.h>
#define Mod 998244353
#define ll long long
#define N 110
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
pair <int, int> f[N][1 << 11][2][2]; int a[N], pw[N]; ll l, r, K;
pair <int, int> dp(int pos, int s, bool lim, bool l, int tot) {
	if (tot > K) return make_pair(0, 0);
	if (pos == -1) return make_pair(!l, 0);
	if (f[pos][s][lim][l].first != -1) return f[pos][s][lim][l];
	int x = (lim) ? a[pos] : 9, s1 = 0, s2 = 0;
	for (int i = 0; i <= x; i++)
		if (!l) {
			pair <int, int> t = dp(pos - 1, s | (1 << i), lim && i == a[pos], l, tot + ((s & (1 << i)) == 0));
			int tx = t.first, ty = t.second;
			s1 = (s1 + tx) % Mod, s2 = (s2 + 1ll * i * pw[pos] % Mod * tx % Mod) % Mod, s2 = (s2 + ty) % Mod;
		} else
			if (i == 0) {
				pair <int, int> t = dp(pos - 1, s, lim && i == a[pos], true, tot);
				int tx = t.first, ty = t.second;
				s1 = (s1 + tx) % Mod, s2 = (s2 + ty) % Mod;
			} else {
				pair <int, int> t = dp(pos - 1, s | (1 << i), lim && i == a[pos], false, tot + 1);
				int tx = t.first, ty = t.second;
				s1 = (s1 + tx) % Mod, s2 = (s2 + 1ll * i * pw[pos] % Mod * tx % Mod) % Mod, s2 = (s2 + ty) % Mod;
			}
	if (!lim && !l) f[pos][s][lim][l] = make_pair(s1, s2);
	return make_pair(s1, s2);
}
int solve(ll n) {
	int len = -1; ll x = n;
	while (x) a[++len] = x % 10, x /= 10;
	memset(f, -1, sizeof(f));
	pair <int, int> tmp = dp(len, 0, 1, 1, 0);
	return tmp.second;
}
int main() {
	read(l), read(r), read(K); pw[0] = 1;
	for (int i = 1; i <= 20; i++) pw[i] = 10ll * pw[i - 1] % Mod;
	cout << (solve(r) - solve(l - 1) + Mod) % Mod << "\n";
	return 0;
}

F. Choosing Two Paths

题面

给定一棵 n n n个点的树,要求找到2条路径 x 1 → y 1 , x 2 → y 2 x_1\rightarrow y_1,x_2\rightarrow y_2 x1y1,x2y2满足这两条路径的端点均不在另一条路径上,在满足路径交最大的情况下满足路径长度之和最大,输出方案。
保证存在合法的两条路径且它们有交。
n ≤ 2 × 1 0 5 n≤2×10^5 n2×105

解法
  • 因为满足至少有交,所以我们可以找到一个度数大于2的点作为根。
  • 考虑枚举最后交的 l c a lca lca,那么我们可以尽量让两个端点在 l c a lca lca的同一个子树里,然后让这两个点的深度尽量大。
  • 那么我们可以记 f [ i ] f[i] f[i]表示以 i i i l c a lca lca的子树中最优的两条路径,辅助数组 g [ i ] g[i] g[i]表示以点 i i i为根的子树中如果存在分叉点,最深的分叉点的子树中最深的两个点是什么。 f f f g g g可以在 d f s dfs dfs的过程中处理出来。
  • 中间可能需要使用sort,时间复杂度: O ( n ) O(n) O(n)~ O ( n log ⁡ n ) O(n\log n) O(nlogn)
代码
#include <bits/stdc++.h>
#define N 200010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
bool fl[N]; int n, cnt, d[N], s[N], mxd[N];
struct Edge {int next, num;} e[N * 3];
struct Info {
	int sx, sy, lca;
	bool operator < (const Info &g) const {
		if (d[lca] != d[g.lca]) return d[lca] > d[g.lca];
		return d[sx] + d[sy] > d[g.sx] + d[g.sy];
	}
} g[N];
struct Node {int x1, y1, x2, y2, ans, len;} f[N];
bool cmp(int x, int y) {return d[mxd[x]] > d[mxd[y]];}
void add(int x, int y) {
	e[++cnt] = (Edge) {e[x].next, y};
	e[x].next = cnt;
}
void dfs(int x, int fa) {
	d[x] = d[fa] + 1, mxd[x] = x; int tot = 0;
	for (int p = e[x].next; p; p = e[p].next) {
		int k = e[p].num; if (k == fa) continue;
		tot++, dfs(k, x);
		if (d[mxd[k]] > d[mxd[x]]) mxd[x] = mxd[k];
	}
	if (tot >= 2) fl[x] = true;
	vector <Info> a; vector <int> b;
	for (int p = e[x].next; p; p = e[p].next) {
		int k = e[p].num; if (k == fa) continue;
		if (fl[k]) fl[x] = true, a.push_back(g[k]);
			else b.push_back(k);
	}
	sort(a.begin(), a.end());
	sort(b.begin(), b.end(), cmp);
	if (a.size() >= 2) {
		Info tx = a[0], ty = a[1]; g[x] = tx;
		int x1 = tx.sx, y1 = tx.sy, x2 = ty.sx, y2 = ty.sy;
		f[x] = (Node) {x1, x2, y1, y2};
		f[x].ans = d[tx.lca] + d[ty.lca] - 2 * d[x];
		f[x].len = d[x1] + d[y1] + d[x2] + d[y2] - 4 * d[x];
		return;
	}
	if (a.size() == 1) {
		Info tx = a[0]; g[x] = tx;
		if (b.size() >= 2) {
			int x1 = tx.sx, y1 = tx.sy, x2 = mxd[b[0]], y2 = mxd[b[1]];
			f[x] = (Node) {x1, x2, y1, y2};
			f[x].ans = d[tx.lca] - d[x];
			f[x].len = d[x1] + d[y1] + d[x2] + d[y2] - 4 * d[x];
		}
		return;
	}
	if (b.size() >= 2) g[x] = (Info) {mxd[b[0]], mxd[b[1]], x};
}
int main() {
	read(n); cnt = n;
	for (int i = 1; i < n; i++) {
		int x, y; read(x), read(y);
		add(x, y), add(y, x); s[x]++, s[y]++;
	}
	int rt;
	for (int i = 1; i <= n; i++) if (s[i] >= 3) rt = i;
	memset(fl, false, sizeof(fl)); dfs(rt, 0);
	int mx1 = -INT_MAX, mx2 = mx1; Node ans;
	for (int i = 1; i <= n; i++)
		if (f[i].ans > mx1) mx1 = f[i].ans, mx2 = f[i].len, ans = f[i];
			else if (f[i].ans == mx1 && f[i].len > mx2) mx2 = f[i].len, ans = f[i];
	cout << ans.x1 << ' ' << ans.y1 << "\n";
	cout << ans.x2 << ' ' << ans.y2 << "\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值