Codeforces Round 1027 (Div. 3)
A. Square Year
题意:判断一个数是不是平方数。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = 0;
for (auto & c : s) {
n = n * 10 + c - '0';
}
int k = std::sqrt(n);
while (k * k < n) {
++ k;
}
if (k * k == n) {
std::cout << 0 << " " << k << "\n";
} else {
std::cout << -1 << "\n";
}
}
B. Not Quite a Palindromic String
题意:给你一个\(01\)串,长度为偶数。把它重新排列能不能使得\(s_i = s_{n-i+1}\)的位置个数恰好为\(k\)。
首先需要拿\(\frac{n}{2}-k\)对\(0, 1\)来匹配为不相等的位置,那么\(cnt_0, cnt_1\)都要大于等于\(\frac{n}{2}-k\)。然后看剩下的相同的两两配对能不能凑\(k\)个。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
int cnt[2]{};
std::string s;
std::cin >> s;
for (auto & c : s) {
++ cnt[c - '0'];
}
cnt[0] -= n / 2 - k;
cnt[1] -= n / 2 - k;
if (cnt[0] < 0 || cnt[1] < 0 || cnt[0] / 2 + cnt[1] / 2 < k) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
}
}
C. Need More Arrays
题意:一个排好序的数组,求一个最长的子序列,使得\(a_i > a_{i-1} + 1\)。
从左到右模拟就行,优先选最小的数,这样让后面的数选择范围更大。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int ans = 1;
for (int i = 1, last = a[0]; i < n; ++ i) {
if (last + 1 < a[i]) {
last = a[i];
++ ans;
}
}
std::cout << ans << "\n";
}
D. Come a Little Closer
题意:给你\(n\)个坐标,移动一个坐标,使得包含这些点的最小矩形的面积最小。
如果不移动,那么就是求最大最小的横纵坐标。
用两个\(multiset\)分别存\(x, y\)坐标,然后枚举删点。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<i64> x(n), y(n);
std::multiset<i64> X, Y;
for (int i = 0; i < n; ++ i) {
std::cin >> x[i] >> y[i];
X.insert(x[i]);
Y.insert(y[i]);
}
if (n == 1) {
std::cout << 1 << "\n";
return;
}
i64 ans = (*X.rbegin() - *X.begin() + 1) * (*Y.rbegin() - *Y.begin() + 1);
for (int i = 0; i < n; ++ i) {
X.extract(x[i]);
Y.extract(y[i]);
i64 a = *X.rbegin() - *X.begin() + 1, b = *Y.rbegin() - *Y.begin() + 1;
i64 sum = a * b;
if (sum < n) {
ans = std::min({ans, sum + a, sum + b});
} else {
ans = std::min(ans, sum);
}
X.insert(x[i]);
Y.insert(y[i]);
}
std::cout << ans << "\n";
}
E. Kirei Attacks the Estate
题意:一棵树,每个点有点权,根为\(1\)。每个点可以向上延伸,一开始加\(a_u\),然后减\(a_{fa_u}\),然后加\(a_{fa_{fa_u}}\),也就是加减交替,终点任意。求每个点可以得到的最大值。
记\(f[u], g[u]\)分别为起点为\(u\)的路径的最大最小值。那么一条路径起点为\(u\),现在起点变为它的子节点\(v\),那么路径上的点权的加减就变换了,也就是这条路径的和乘\(-1\)。这样就能得到\(f[v], g[v]\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
const i64 inf = 1e18;
std::vector<i64> f(n), g(n);
auto dfs = [&](auto & self, int u, int fa) -> void {
f[u] += a[u];
g[u] += a[u];
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
f[v] = std::max({0ll, -f[u], -g[u]});
g[v] = std::min({-f[u], -g[u]});
self(self, v, u);
}
};
dfs(dfs, 0, -1);
for (int i = 0; i < n; ++ i) {
std::cout << std::max(f[i], g[i]) << " \n"[i == n - 1];
}
}
F. Small Operations
题意:两个数\(x, y\)。\(x\)每次可以乘\([1, k]\)里的一个数,或者在\([1, k]\)里选一个\(x\)的因子把它除掉。求\(x\)变成\(y\)的最小操作数。
从质因子分解来看,其实就是\(x\)比\(y\)多了一些质因子,也少了一些质因子,需要先把多的除掉,然后把少的乘起来。这两部分都可以\(dp\)。直接预处理每个数的因子,然后假设现在要凑出\(n\)来,那么对于\(n\)的每个因子之间转移就行,因为范围只有\(1e6\),因子最多的数只有两百多个。
点击查看代码
std::vector<std::vector<int>> g;
void init(int n) {
g.assign(n + 1, {});
for (int i = 1; i <= n; ++ i) {
for (int j = i; j <= n; j += i) {
g[j].push_back(i);
}
}
}
void solve() {
int x, y, k;
std::cin >> x >> y >> k;
if (k == 1) {
std::cout << (x == y ? 0 : -1) << "\n";
return;
}
int d = std::gcd(x, y);
x /= d, y /= d;
const int inf = 1e9;
auto work = [&](int n) -> int {
int m = g[n].size();
std::vector<int> f(m, inf);
f[0] = 0;
for (int i = 0; i < m; ++ i) {
if (f[i] == inf) {
continue;
}
for (int j = i + 1; j < m; ++ j) {
if (g[n][j] % g[n][i] == 0 && g[n][j] / g[n][i] <= k) {
f[j] = std::min(f[j], f[i] + 1);
} else if (g[n][j] / g[n][i] > k) {
break;
}
}
}
return f[m - 1] == inf ? -1 : f[m - 1];
};
int ans1 = work(x), ans2 = work(y);
if (ans1 == -1 || ans2 == -1) {
std::cout << -1 << "\n";
} else {
std::cout << ans1 + ans2 << "\n";
}
}
G. Build an Array
待补

浙公网安备 33010602011771号