D.Digital string maximization
- 贪心地考虑,让靠前的位置更大。可枚举后面所有位置,将其依次按题意移动至该位置尝试。
- 观察发现,由于数字仅有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
- c中的一个字符,要么来自a,要么来自b,要么是第二步操作得来的。
- 考虑dp,dp[i][j] 表示 a 串用了 i 个,b 串用了 j 个的条件下,得到合法的 c 字符串至少需要多少次第二步操作。
- 如果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
- 题目的询问方式很像数据结构题的询问方式,单次询问必须 o ( l o g n ) o(logn) o(logn)处理。
- 求模数后结果相同,不好处理。但是如果模后都为0,就变为区间最大公因数问题了,很好处理。
- 求区间最大公因数是“可重复贡献问题”(PS:指一个元素重复计算多次,和只计算一次效果相同。比如求多个数的最大值就是“”可重复贡献问题”,max{1,2 , 3}和max{1,2,3,3} 、max{1,1,2 , 3}的结果一样,3是重复的)可以用st表(又称RMQ)解决
- 注意到,如果 a、b取模后结果相同,那么a-b取模后结果为0。如果 a、b、取模后结果相同,那么a-b 和 b-c 取模后结果都为0。可以通过作差,把求模数后结果相同变为区间最大公因数问题。
- 把考虑对整个数组做差分,求其差分数组,那么问 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)只删除他一个点。
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;
}