Prison Break
题意是,给你一个 \(n \times m\) 的矩阵,然后让你求这个矩阵中到 \((r, w)\) 的最远距离是多大。
模拟即可。
#include <cstdio>
using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d%d", &a, &b, &c, &d);
int ans1 = c - 1 + abs(b - d);
int ans2 = c - 1 + d - 1;
int ans3 = abs(a - c) + d - 1;
int ans4 = abs(a - c) + abs(b - d);
cout << max(ans1, max(ans2, max(ans3, ans4))) << "\n";
}
}
Repainting Street
一共有 n 个颜色,每次可以将最多 k 个方块粉刷成多种颜色,然后问你最少刷多少次之后颜色都相同。
可以发现颜色数很少,可以枚举每个的颜色数,暴力算将所有方块都染成这种颜色需要的方案书就行了,对答案取最小值。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
#define M 1010
using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int T;
int n, m, a[N];
inline int read() {
char c = getchar(); int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int main() {
T = read();
while (T--) {
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read();
int minn = inf;
for (int i = 1; i <= 300; i++) {
int sum = 0;
for (int j = 1; j <= n; j++)
if (a[j] != i) {
sum++;
j = j + m - 1;
}
minn = min(minn, sum);
}
cout << minn << "\n";
}
}
Bouncing Ball
猜俩结论,显然操作 \(1\) 仅会修改对答案有贡献的位置。且操作的顺序一定是若干次操作 \(2\) 加上若干次操作 \(1\)。
第二个结论正确性显然,若先进行一次操作 \(1\) 再进行操作 \(2\),之前修改的 \(1\) 可能会移动到对答案没有贡献的位置上。不如先进行操作 \(2\),再修改对答案有贡献的位置。
数据范围不大,考虑枚举操作 \(2\) 进行的次数 \(i\),新的对答案有贡献的位置为初始序列的 \((p+i)+q \times k\)。
发现步长没有改变,考虑预处理 \(f_j\) 表示 \((p+i)=j\) 时,有贡献位置上 \(0\) 的个数,显然有:
则操作 \(2\) 进行 \(i\) 次的最小代价为 \(f_{p+i} \times x+i\times y\),取最小值即为答案。
总时间复杂度 \(\operatorname{O}{n}\)。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
#define N 100010
#define M 1010
using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f3f3f3f3f;
int T, n, p, k;
int a[N], num[N], x, y;
int sum[N];
inline int read() {
char c = getchar(); int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
signed main() {
T = read();
while (T--) {
memset(a, 0, sizeof a);
memset(sum, 0, sizeof sum);
memset(num, 0, sizeof num);
n = read(), p = read(), k = read();
for (int i = 1; i <= n; i++) {
char sy;
cin >> sy;
a[i] = sy - '0';
}
x = read(), y = read();
int ans = inf;
for (int i = n; i >= 1; i--)
if (a[i] == 0) sum[i] = sum[i + k] + 1;
else sum[i] = sum[i + k];
for (int i = 0; i <= n - p; i++)
ans = min(ans, sum[i + p] * x + i * y);
cout << ans << "\n";
}
}
XOR-gun
结论,暴力。
有一些显然的性质。
-
使得该数列 不单调不降,即存在合并后的两个位置,使得前一个 大于 后一个,以下称它为断点。
-
被操作的位置一定是包含上述断点的一段区间,该断点前的部分构成较大的数,该断点后的部分构成较小的数。正确性显然。为了保证代价最小,断点不可能存在两个。为了使断点位置满足条件,需要对它前后分别操作。
于是可以考虑暴力枚举断点以及左右的区间,求得最小代价,复杂度 \(\operatorname{O}(n^3)\)。
观察题目的特殊性质,从位运算的角度考虑,可以发现:
如果有三个连续的数的最高位相同,则可将后面两个异或起来消去最高位,使得第一个数大于后面的数。仅需操作 \(1\) 次即可。
又数列单调不降,则最长的使得三个连续的数最高位不同的数列长度为 \(2\times30\)。
特判 \(n>60\) 时输出 \(1\) 即可通过本题。
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define N 70
using namespace std;
int n, ans = N, a[N];
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
int main() {
n = read();
if (n > 60) {
printf("1\n");
return 0;
}
for (int i = 1; i <= n; ++ i)
a[i] = a[i - 1] ^ read();
for (int l = 1; l < n - 1; ++ l)
for (int r = l; r < n; ++ r)
for (int k = r + 1; k <= n; ++ k)
if ((a[r] ^ a[l - 1]) > (a[k] ^ a[r]))
ans = min(ans, (r - l) + (k - r - 1));
printf("%d\n", (ans == N) ? -1 : ans);
return 0;
}
New Game Plus!
咕咕咕~~
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 500010
using namespace std;
const ll inf = 1e18;
int n, k, a[N];
ll ans = -inf, suml, sumr, sum[N], val[N];
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
int main() {
n = read(), k = read() + 1;
for (int i = 1; i <= n; i++) a[i] = read();
sort(a + 1, a + n + 1);
for (int i = n; i >= 1; i--) sumr += 1ll * (i - 1) * a[i], sum[i] = sum[i + 1] + a[i];
for (int i = 1, j = 1, lth = 0; i <= n; i++, j++) {
if (j == k + 1) j = 1, lth++;
ans = max(ans, suml + 1ll * lth * sum[i] + sumr);
suml -= val[j];
val[j] += 1ll * lth * a[i];
suml += val[j];
sumr -= sum[i + 1];
}
printf("%lld\n", ans);
return 0;
}
鸣谢
Luckyblock