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