Codeforces Round 991 (Div. 3) 【题解】D~G

比赛地址传送门

D.Digital string maximization

  1. 贪心地考虑,让靠前的位置更大。可枚举后面所有位置,将其依次按题意移动至该位置尝试。
  2. 观察发现,由于数字仅有1~9,往后枚举10个位置一定可找到答案。
#include <bits/stdc++.h>
#define int long long

using namespace std;
string s;

void solve() {
	cin >> s;
	int n = s.size();
	s = "#" + s;
	for (int i = 1; i <= n; i ++) {
		int mx = s[i] - '0', idx = i;
		for (int j = i + 1; j <= min(n, i + 10); j ++) {
			if (s[j] - (j - i) - '0' > mx) {
				idx = j;
				mx = s[j] - (j - i) - '0';
			}
		}
		for (int j = idx; j > i; j --) {
			s[j] --;
			swap(s[j - 1], s[j]);
		}
		cout << s[i];
	}
	cout << '\n';

}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	cin >> t;
	while (t --) {
		solve();
	}
	return 0;
}

E.Three Strings

  1. c中的一个字符,要么来自a,要么来自b,要么是第二步操作得来的。
  2. 考虑dp,dp[i][j] 表示 a 串用了 i 个,b 串用了 j 个的条件下,得到合法的 c 字符串至少需要多少次第二步操作。
  3. 如果c[i + j + 1]恰好和a\b串的字母相同,不需要操作,反正需要操作。具体更新如下:
			if (i + 1 < a.size()) {
				if (a[i + 1] == c[i + j + 1])
					dp[i + 1][j] = min(dp[i + 1][j], dp[i][j]);
				else
					dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);
			}
			if (j + 1 < b.size()) {
				if (b[j + 1] == c[i + j + 1])
					dp[i][j + 1] = min(dp[i][j + 1], dp[i][j]);
				else
					dp[i][j + 1] = min(dp[i][j + 1], dp[i][j] + 1);
			}

完整代码:

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> PII;

const int N = 2e3 + 10;
string a, b, c;
int dp[N][N];

void solve() {
	cin >> a >> b >> c;
	a = "#" + a;
	b = "#" + b;
	c = "#" + c;
	for (int i = 0; i < a.size(); i ++)
		for (int j = 0; j < b.size(); j ++) {
			dp[i][j] = 1e18;
		}
	dp[0][0] = 0;
	for (int i = 0; i < a.size(); i ++)
		for (int j = 0; j < b.size(); j ++) {
			if (i + 1 < a.size()) {
				if (a[i + 1] == c[i + j + 1])
					dp[i + 1][j] = min(dp[i + 1][j], dp[i][j]);
				else
					dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);
			}
			if (j + 1 < b.size()) {
				if (b[j + 1] == c[i + j + 1])
					dp[i][j + 1] = min(dp[i][j + 1], dp[i][j]);
				else
					dp[i][j + 1] = min(dp[i][j + 1], dp[i][j] + 1);
			}

		}
	cout << dp[a.size() - 1][b.size() - 1] << '\n';

}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	cin >> t;
	while (t --) {
		solve();
	}
	return 0;
}

F. Maximum modulo equality

  1. 题目的询问方式很像数据结构题的询问方式,单次询问必须 o ( l o g n ) o(logn) o(logn)处理。
  2. 求模数后结果相同,不好处理。但是如果模后都为0,就变为区间最大公因数问题了,很好处理。
  3. 求区间最大公因数是“可重复贡献问题”(PS:指一个元素重复计算多次,和只计算一次效果相同。比如求多个数的最大值就是“”可重复贡献问题”,max{1,2 , 3}和max{1,2,3,3} 、max{1,1,2 , 3}的结果一样,3是重复的)可以用st表(又称RMQ)解决
  4. 注意到,如果 a、b取模后结果相同,那么a-b取模后结果为0。如果 a、b、取模后结果相同,那么a-b 和 b-c 取模后结果都为0。可以通过作差,把求模数后结果相同变为区间最大公因数问题。
  5. 把考虑对整个数组做差分,求其差分数组,那么问 l , r 的时候,求差分数组对于区间的最大公因数即可
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int N = 2e5 + 10, M = 19;
int a[N], b[N];
int n, q;
int f[N][M];

void init() {
	for (int i = 0; i < M; i ++) {
		for (int j = 1; j + (1ll << i) - 1 <= n; j ++) {
			if (!i) {
				f[j][i] = b[j];
			} else {
				int ne = j + (1ll << (i - 1));
				f[j][i] = __gcd(f[j][i - 1], f[ne][i - 1]);
			}
		}
	}
}


void solve() {
	cin >> n >> q;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i ++) {
		b[i] = abs(a[i] - a[i - 1]);
	}
	init();
	while (q --) {
		int l, r;
		cin >> l >> r;
		if (l == r)
			cout << 0 << ' ';
		else {
			l ++;
			int k = log2(r - l + 1);
			cout << __gcd(f[l][k], f[r - (1ll << k) + 1][k]) << ' ';
		}
	}
	cout << '\n';
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	cin >> t;
	while (t --) {
		solve();
	}
	return 0;
}

G. Tree Destruction

  1. 最终选择删掉的,是树上的一条路径。
  2. 最终的答案和树上点的度有紧密关系。
  3. 考虑枚举每一个点,找到经过点的所有路径中,结果最大的一条即可。
  4. 经过一个点的路径有三种,1)直达该点不拐弯的直线,2)在该点拐弯的直线 3)只删除他一个点。

在这里插入图片描述
5. 考虑dfs,返回值是删去经过该点的一条(1)类或(3)类线时,得到的最终答案。
则父节点的(1)类线为子节点返回的最大值+该父节点的度-2,父节点(2)类线为子节点返回的最大值 + 次大值 +父节点的度 - 4。统计答案即可。
DFS更新:

int dfs(int x, int last) {
	vector<int> a;
	for (auto i : v[x]) {
		if (i != last) {
			a.push_back(dfs(i, x));
		}
	}
	if (a.size() == 0) {
		return st[x];
	}
	sort(a.begin(), a.end(), greater<int>());
	ans = max(ans, st[x]);
	if (a.size() >= 1)
		ans = max(ans, a[0] + st[x] - 2);
	if (a.size() >= 2)
		ans = max(ans, a[0] + a[1] + st[x] - 4);

	return max(a[0] + st[x] - 2, st[x]);
}

完整代码:

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> PII;
const int N = 4E5 + 10;
vector<int> v[N];
int n;
int st[N];
long long ans;

int dfs(int x, int last) {
	vector<int> a;
	for (auto i : v[x]) {
		if (i != last) {
			a.push_back(dfs(i, x));
		}
	}
	if (a.size() == 0) {
		return st[x];
	}
	sort(a.begin(), a.end(), greater<int>());
	ans = max(ans, st[x]);
	if (a.size() >= 1)
		ans = max(ans, a[0] + st[x] - 2);
	if (a.size() >= 2)
		ans = max(ans, a[0] + a[1] + st[x] - 4);

	return max(a[0] + st[x] - 2, st[x]);
}

void solve() {
	cin >> n;
	ans = 0;
	for (int i = 1; i <= n; i ++) {
		v[i].clear();
		st[i] = 0;
	}
	for (int i = 1; i <= n - 1; i ++) {
		int x, y;
		cin >> x >> y;
		v[x].push_back(y), v[y].push_back(x);
		st[x] ++, st[y] ++;
	}
	dfs(1, -1);
	cout << ans << '\n';
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	cin >> t;
	while (t --) {
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值