A.Christmas Present(判断)
题意:
给出球拍和手套的价格,哪个贵买哪个,要求输出购买的物品。
分析:
判断输出即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5e2;
void solve() {
int B, G;
cin >> B >> G;
if (B > G) {
cout << "Bat" << endl;
} else {
cout << "Glove" << endl;
}
}
int main() {
int Case = 1;
while (Case--) {
solve();
}
return 0;
}
B.Christmas Trees(数学)
题意:
有一条无限长的路,路上的AAA种了一棵树,并以AAA点为中心,每隔kkk米均有一棵树,问L∼RL \sim RL∼R之间共有多少棵树(包含边界)。
分析:
首先可以通过将区间左右端点减去AAA,此时中心点就变为了原点。
然后考虑两种情况:
-
左右边界均在原点一侧
-
左右边界在原点两侧
如果左右边界在原点一侧,可以直接通过计算得到区间内的树的数量(左右边界均为负数时可以以原点为中心翻转)
如果左右边界在原点两侧,那么可以分别计算两边到原点之间有多少棵树。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5e2;
void solve() {
LL A, M, L, R;
cin >> A >> M >> L >> R;
L -= A, R -= A;
LL ans = 0;
if (L < 0 && R < 0) {
L = -L, R = -R;
swap(L, R);
}
if (L >= 0) {
ans = R / M - L / M;
if (L % M == 0) ans++;
} else {
ans = R / M + (-L) / M + 1;
}
cout << ans << endl;
}
int main() {
int Case = 1;
while (Case--) {
solve();
}
return 0;
}
C.Socks 2(前/后缀和,枚举)
题意:
有NNN双袜子,编号为1∼N1 \sim N1∼N,其中有kkk双袜子丢失了,编号为A1,A2,...,AkA_1, A_2, ..., A_kA1,A2,...,Ak,当穿上两只不同的袜子时,古怪值为这两只袜子编号之差的绝对值,问,如果将剩下的袜子两两配对(如果总数为奇数,可以丢掉一只袜子),最小的古怪值是多少。
分析:
对于没有发生丢失的袜子,不需要进行操作。
对于发生丢失的袜子,分为两种情况:
-
丢失的袜子数量为偶数
-
丢失的袜子数量为奇数
当丢失的袜子数量为偶数时,由于给出的袜子编号是有序的,那么将袜子按顺序进行匹配,古怪值为(A2−A1)+...+(Ak−Ak−1)(A_2 - A_1) + ... + (A_k - A_{k - 1})(A2−A1)+...+(Ak−Ak−1)。
当丢失的袜子数量为奇数时,由于无法知道丢掉哪只袜子是最优的,因此,可以对丢掉的袜子进行枚举,丢掉一只袜子后,剩余的袜子按偶数的情况进行计算,为了避免超时,可以预处理前缀和和后缀和,如果前后两段袜子数量恰好为奇数,那么就让前一段的最后一只袜子与后一段的第一只袜子匹配即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5e2;
int a[N], L[N], R[N];
void solve() {
int n, k;
cin >> n >> k;
for (int i = 1; i <= k; i++) cin >> a[i];
int ans = 0;
for (int i = 1; i <= k; i += 2) {
ans += a[i + 1] - a[i];
}
L[2] = a[2] - a[1];
for (int i = 4; i < k; i += 2) {
L[i] = a[i] - a[i - 1] + L[i - 2];
}
for (int i = k - 1; i >= 1; i -= 2) {
R[i] = a[i + 1] - a[i] + R[i + 2];
}
if (k % 2 == 1) {
int res = 1e9;
for (int i = 1; i <= k; i++) {
if (i % 2 == 1) {
if (i == 1) res = min(res, R[i + 1]);
else res = min(res, L[i - 1] + R[i + 1]);
} else {
int num = L[i - 2] + R[i + 2] + a[i + 1] - a[i - 1];
res = min(res, num);
}
}
cout << res << endl;
} else {
cout << ans << endl;
}
}
int main() {
int Case = 1;
while (Case--) {
solve();
}
return 0;
}
D.Reindeer and Sleigh(前缀和,二分)
题意:
有NNN个雪橇,第iii个雪橇需要RiR_iRi头驯鹿才能拉动。
有QQQ个询问,每个询问会给出一个正整数XXX,问有XXX头驯鹿时最多可以拉动多少个雪橇。
分析:
既然想要拉动的雪橇数量尽可能多,那么应该从小到大的选择雪橇需要的驯鹿数量,因此可以先对需要的驯鹿数量排序,然后维护前缀和,之后的查询使用二分来进行。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5e2;
LL R[N], pre[N];
void solve() {
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> R[i];
}
sort(R + 1, R + n + 1);
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + R[i];
}
while (q--) {
LL x;
cin >> x;
int l = 0, r = n, ans = 0;
while (l <= r) {
int mid = l + r >> 1;
if (pre[mid] <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans << endl;
}
}
int main() {
int Case = 1;
while (Case--) {
solve();
}
return 0;
}
E.Christmas Color Grid 1(DFS)
题意:
有一个HHH行WWW列的网格,其中每个单元格被涂成红色或绿色。
设(i,j)(i,j)(i,j)表示从上到下第iii行,从左到右第jjj列的单元格。
单元格(i,j)(i,j)(i,j)的颜色由字符Si,jS_{i,j}Si,j表示,Si,j="."S_{i,j}="."Si,j="."表示单元格(i,j)(i,j)(i,j)是红色的,Si,j="."S_{i,j}="."Si,j="."表示单元格(i,j)(i,j)(i,j)是绿色的。
定义绿色连通分量是指顶点集是绿色单元格、边缘集为连接两个相邻绿色单元格的单元格的集合。当∣x−x′∣+∣y−y′∣=1\lvert x−x^′\rvert + \lvert y−y^′\rvert=1∣x−x′∣+∣y−y′∣=1时,单元格(x,y)(x,y)(x,y)和(x′,y′)(x',y')(x′,y′)被认为是相邻的。
选择一个红色单元格并随机地将其重新涂成绿色。计算重新涂色后网格中绿色连通分量数量的期望值,结果对998244353998244353998244353取模。
分析:
对于每个红色方块,考虑将其重新涂成绿色后连通分量数量的差异。
固定一个红色方块(i,j)(i,j)(i,j),假设有xxx个绿色连通分量与单元格(i,j)(i,j)(i,j)相邻。通过将(i,j)(i,j)(i,j)涂成绿色,所有与(i,j)(i,j)(i,j)相邻的绿色方块都与之相连,因此包含(i,j)(i,j)(i,j)或者与之相邻的方块的绿色联通分量数量从xxx变为111。其他地方的连通分量数量不变,所以总数减少了(x−1)(x-1)(x−1)个。(当x=0x=0x=0 时,可以认为是减少了−1-1−1个,即增加了111个)。
由于网格大小只有1000×10001000 \times 10001000×1000,因此可以枚举每个红色单元格,分别计算其涂色后的影响,结果求平均值即可,可以使用并查集或者DFSDFSDFS进行维护。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1005;
const LL mod = 998244353;
char c[MAXN][MAXN];
LL a[MAXN][MAXN], cnt, n, m;
void dfs(int i, int j) {
if (a[i][j] || c[i][j] != '#')
return;
a[i][j] = cnt;
dfs(i + 1, j);
dfs(i, j + 1);
dfs(i - 1, j);
dfs(i, j - 1);
}
int gt(int x, int y) {
if (x > n || x < 1 || y > m || y < 1 || c[x][y] == '.')
return 0;
return a[x][y];
}
int get(int x, int y) {
map<int, int> mp;
if (gt(x + 1, y))
mp[gt(x + 1, y)] = 1;
if (gt(x, y + 1))
mp[gt(x, y + 1)] = 1;
if (gt(x - 1, y))
mp[gt(x - 1, y)] = 1;
if (gt(x, y - 1))
mp[gt(x, y - 1)] = 1;
return 1 - mp.size();
}
void exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1;
y = 0;
} else {
exgcd(b, a % b, y, x);
y -= a / b * x;
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> c[i][j];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (c[i][j] == '#' && !a[i][j]) {
++cnt, dfs(i, j);
}
}
}
LL p = 0, q = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (c[i][j] == '.') {
q++;
p += cnt + get(i, j);
p %= mod;
}
}
}
LL k = __gcd(p, q);
p /= k;
q /= k;
LL x, y;
exgcd(q, mod, x, y);
x = (x % mod + mod) % mod;
cout << (x * p) % mod << endl;
return 0;
}
F.Christmas Present 2(动态规划)
题意:
圣诞老人住在一个用平面坐标系xyxyxy表示的小镇上,小镇有NNN个孩子,编号为111到NNN。圣诞老人的家坐标为(SX,SY)(S_X,S_Y)(SX,SY),孩子iii (1≤i≤N)(1\le i \le N)(1≤i≤N)的家在坐标(Xi,Yi)(X_i,Y_i)(Xi,Yi)。
圣诞老人想按数字的顺序给NNN个孩子每人送一份礼物。为了给孩子iii送礼物,圣诞老人必须带着至少一件礼物去拜访孩子iii的家。然而,圣诞老人一次最多只能携带KKK个礼物,把身上的礼物送完后,他必须回到自己的家补充礼物(圣诞老人家里有足够的礼物)。
求出圣诞老人离开家,给所有NNN个孩子送礼物,然后返回家的最小路程。
分析:
对于每个孩子iii,在给孩子iii送礼物之后和给孩子(i+1)(i+1)(i+1)送礼物之前有两条可能的路径:
- 直接从房子iii到房子(i+1)(i +1)(i+1);
- 或者从房子iii回到圣诞老人的房子,然后前往房子i+1i+1i+1。
用序列did_idi,表示从圣诞老人家走到第iii个点的距离,sum(i→j)sum(i→j)sum(i→j)表示从房子iii走到房子jjj的距离。
采用动态规划对其进行求解。令dpidp_idpi表示前iii个孩子走完的最小花费。枚举jjj,dpi=min[dpj+d(j+1)+d(i)+sum(j+1→i)]dp_i=min[dp_j+d(j+1)+d(i)+sum(j+1→i)]dpi=min[dpj+d(j+1)+d(i)+sum(j+1→i)]。因为圣诞老人最多可以携带KKK个礼物,所以(i−j≤k)(i−j≤k)(i−j≤k)。可以使用单调队列或者线段树进行优化。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 5;
LL a[MAXN];
double d[MAXN], dp[MAXN], s[MAXN], sum[MAXN];
struct edge {
double x, y;
} ed[MAXN];
double solve(double a, double b, double c, double d) {
return sqrt((a - c) * (a - c) + (b - d) * (b - d));
}
int main() {
int n, c;
double sx, sy;
cin >> n >> c >> sx >> sy;
for (int i = 1; i <= n; i++)
cin >> ed[i].x >> ed[i].y;
for (int i = 1; i <= n; i++)
dp[i] = 0x3f3f3f3f;
dp[0] = 0;
ed[0].x = sx;
ed[0].y = sy;
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + solve(ed[i - 1].x, ed[i - 1].y, ed[i].x, ed[i].y);
d[i] = solve(sx, sy, ed[i].x, ed[i].y);
}
int hd, tl;
hd = tl = 1;
a[1] = 0;
for (int i = 1; i <= n; i++) {
while (hd <= tl && i - a[hd] > c)
hd++;
if (hd <= tl)
dp[i] = dp[a[hd]] + d[i] + d[a[hd] + 1] + sum[i] - sum[a[hd] + 1];
while (hd <= tl && dp[a[tl]] + d[a[tl] + 1] - sum[a[tl] + 1] > dp[i] + d[i + 1] - sum[i + 1])
tl--;
a[++tl] = i;
}
cout << fixed << setprecision(8) << dp[n] << endl;
return 0;
}
学习交流
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

圣诞节编程挑战:礼物分配、连通组件与动态规划,
1021

被折叠的 条评论
为什么被折叠?



