题意 & 题解
A.Prison Break
题意:
在 (n imes m) 的二维平面中找到距离 ((r,c)) 最大的曼哈顿距离。
题解:
横向移动最多为 (max(r-1,n-r)),纵向移动最多为 (max(c-1,m-c))
一定在矩阵的四个角上。
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100001
#define inf 2147483647
typedef long long ll;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
inline void read(int &T) {
int x = 0; bool f = 0; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = !f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
T = f ? -x : x;
}
int t, n, m, r, c;
int main() {
read(t);
while (t--) {
int ans = 0;
read(n), read(m), read(r), read(c);
ans = max(ans, abs(1 - r) + abs(1 - c));
ans = max(ans, abs(n - r) + abs(1 - c));
ans = max(ans, abs(1 - r) + abs(m - c));
ans = max(ans, abs(n - r) + abs(m - c));
printf("%d
", ans);
}
return 0;
}
B.Repainting Street
题意:
给出 (n) 个点和一个参数 (k),每个点有一个颜色 (a_i),每次可以选择一段长为 (k) 的区间任意修改其中的颜色,问最少选择多少段使得颜色全部一致。
(1le a_i le 100),(1 le n le 10^5)
题解:
枚举改成什么颜色,然后贪心,遇到一个不是该颜色的点就从该点开始向右选择长为 (k) 的区间,时间复杂度 (O(nmax(a_i)))。
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100001
#define inf 2147483647
typedef long long ll;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
inline void read(int &T) {
int x = 0; bool f = 0; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = !f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
T = f ? -x : x;
}
int t, n, k, ans, a[M];
int main() {
read(t);
while (t--) {
read(n), read(k); ans = inf;
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= 100; ++i) {
int tot = 0, last = -1;
for (int j = 1; j <= n; ++j) {
if (a[j] == i) continue;
if (last == -1) ++tot, last = j;
else {
if (last + k - 1 >= j) continue;
else last = j, ++tot;
}
}
ans = min(ans, tot);
}
printf("%d
", ans);
}
return 0;
}
C.Bouncing Ball
题意:
给一个长为 (n) 的 (01) 串,可以进行两种操作:
-
花费 (y) 的时间从最前面删去一个字符
-
花费 (x) 的时间将一个 (0) 变成 (1)。
问最少多少时间之后 (p,p+k,p+2k,dots,p+qk)(其中 (q) 是最大的正整数使得 (p+qk le n))都是 (1)。
题解:
从前面删去一个字符相当于让 (p) 往后移了一个位置,所以可以计算出从 (p,p+1,dots,n) 开始的时间花费(计算有多少个在路径上的点为 (0))。
从 (x) 的位置开始则需要删除 (x - p) 个字符,时间花费为 ((x - p) imes y)。
然后计算 (x,x+k,x+2k,dots,x+qk) 位置上有 (cnt_0) 个字符 (0),时间花费为 (cnt_0 imes x)。
用 (ans_i) 表示从 (i) 开始的 (cnt_0)。
发现 (j) 位置如果为 (0),会对所有 ((x - p)mod k = (j-p) mod k) 的 (x) 有贡献(其中 (x < j)),因为前面以 (x) 为起点的路径包含了后面以 (j) 为起点的路径,可以从后往前 (O(n)) 处理出 (ans) 数组。
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100001
#define inf 2147483647
typedef long long ll;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
inline void read(int &T) {
int x = 0; bool f = 0; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = !f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
T = f ? -x : x;
}
std::string s;
int t, n, p, k, x, y, ans[M];
int main() {
read(t);
while (t--) {
read(n), read(p), read(k);
std::cin >> s;
read(x), read(y);
int len = s.length(), minn = inf;
for (int i = len - 1; i >= p - 1; --i) {
int j = i + 1;
if (s[i] == '1') {
minn = min(ans[(j - p) % k] * x + (j - p) * y, minn);
}
else {
++ans[(j - p) % k];// 余数向同的路径是同一条。
minn = min(ans[(j - p) % k] * x + (j - p) * y, minn);
}
}
std::cout << minn << '
';
memset(ans, 0, sizeof ans);
}
return 0;
}
D.XOR-gun
题意:
给定一个不减的序列,每次可以选择两个相邻的数将这两个数替换为他们的异或值,序列的长度减一,问最少几次操作可以使这个序列不再满足不减。
题解:
对于三个相邻的二进制第一个不为 (0) 的位相同的数,可以选择后两个异或起来,因此想要原数列中不出现这样的情况长度不能超过 (60),如果超过 (60) 直接输出 (1)。
否则暴力。
经过多次操作之后一定是将 (isim j) 与 ((j+1)sim k) 的异或和相比较,枚举 (i,j,k),用前缀异或优化,时间复杂度 (O(n^3))
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100001
#define inf 2147483647
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
int n, ans = inf, a[M], sxor[M];
int main() {
scanf("%d", &n);
if (n >= 100) { puts("1"); return 0; }
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
sxor[i] = sxor[i - 1] ^ a[i];
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
for (int k = i + 1; k <= n; ++k) {
if ((sxor[i] ^ sxor[j - 1]) > (sxor[k] ^ sxor[i])) {
ans = min(ans, k - i - 1 + i - j);
}
}
}
}
if (ans == inf) puts("-1");
else printf("%d
", ans);
return 0;
}
E.New Game Plus!
题意:
初始分数为 (0),奖励值为 (0),有 (n) 个 (boss),每个 (boss) 有一个增量 (a_i),打死一个 (boss) 后可以给分数加上现在的奖励值,并给奖励值加上 (boss) 的增量,你最多可以 (k) 次将奖励值变成 (0)。问能获得的最大得分。
题解:
题目可以转化成将 (n) 个 (boss) 分成 (k+1) 组,每组都是独立的。
对于每一组都按 (boss) 的增量降序排列为 (b_1,b_2,dots,b_m)。
能获得的分数为 ((m-1)b_1+(m-2)b_2+dots +b_{m-1})。
将 (k-1) 个零放到单调队列里,按增量从大到小枚举 (boss),每次从单调队列中取出最大的数加到分数中并把最大的数加上当前增量放到单调队列中。
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#define M 500001
typedef long long ll;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
ll ans;
int n, k, a[M];
std::priority_queue<ll> q;
inline void read(int &T) {
int x = 0; bool f = 0; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = !f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
T = f ? -x : x;
}
bool cmp(int a, int b) { return a > b; }
int main() {
read(n), read(k);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= k + 1; ++i) q.push(0);
std::sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; ++i) {
ll u = q.top(); q.pop();
ans += u, q.push(u + a[i]);
}
std::cout << ans << '
';
return 0;
}
rating & 总结
-
B、C 做的太慢。
-
找性质能力还不够