GDCPC2023 B , D , F , K 题解
和队友一起打的 2023 年广东省大学生程序设计竞赛重现赛,写了 B, D, K,胡了一个 F。
D
题目大意
随着广东的建设与发展,越来越多人选择来到广东开始新生活。在一片新建的小区,有
有的人喜欢自己有邻居,而有的人不喜欢。对于第
您作为小区的规划者,需要最大化所有人的总满意度。
题解
可以发现最优解一定是:
AAA.B.B.B
或者:
B.B.B.B.B.B
或:
AAAAAA
其中 A
和 B
分别表示有邻居的人和没有邻居的人。
让我们贪心地想,这里面的 A
一定是那些 A
,把他放到 B
中对答案的贡献显然更大。
所以按照
最后还要特判全是 A
和全是 B
的情况。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int n, m;
struct node {
int a, b;
inline bool operator < (const node &w) const {
return (b - a) < (w.b - w.a);
}
} a[N];
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> a[i].a >> a[i].b;
sort(a + 1, a + 1 + n);
vector <long long> pre(n + 1, 0), suf(n + 2, 0);
for (int i = 1; i <= n; ++i) pre[i] = pre[i - 1] + a[i].a;
for (int i = n; i; --i) suf[i] = suf[i + 1] + a[i].b;
long long ans = 0;
for (int i = 0; i <= n; ++i) {
if (i == 1) continue;
int need = i + (n - i) * 2 - (!i);
if (need <= m) ans = max(ans, pre[i] + suf[i + 1]);
} cout << ans << '\n';
}
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int T; cin >> T; while (T--) solve();
}
K
题目大意
独立钻石
是一种单人桌游。游戏在
在游戏中,玩家可以选择一枚棋子,将它跳过相邻棋子到空格上,并移除被跳过的棋子。具体来说,令
给定一个初始的棋盘,求经过任意次操作(包括零次)之后,棋盘上最少能剩余几枚棋子。
题解
由于
细节具体请看代码实现。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 7;
int n, m, k;
int a[N][N]; //a[i][j] 表示 (i, j) 的信息,如果为 0 表示这个格子为空,否则表示这个格子上有哪个棋子。
bool vis[N]; //vis[i] 表示第 i 个棋子是否被选过。
struct node {
int x, y;
node (int xx, int yy) {x = xx, y = yy;}
} ; //记录棋子坐标
vector <node> v;
int dx[4] = {0, 0, -1, 1}, dy[4] = {1, -1, 0, 0};
int ans = 0;
void dfs(int cnt) {
ans = min(ans, cnt);
for (int i = 0; i < k; ++i) if (!vis[i]) {
for (int j = 0; j < 4; ++j) {
int nx = v[i].x + dx[j], ny = v[i].y + dy[j];
if (nx && ny && nx <= n && ny <= m && a[nx][ny] != 0) {
int tx = nx + dx[j], ty = ny + dy[j];
if (tx && ty && tx <= n && ty <= m && a[tx][ty] == 0) {
a[tx][ty] = i + 1; a[v[i].x][v[i].y] = 0;
vis[a[nx][ny] - 1] = 1;
int nt = a[nx][ny]; a[nx][ny] = 0;
swap(v[i].x, tx); swap(v[i].y, ty);
dfs(cnt - 1);
swap(v[i].x, tx); swap(v[i].y, ty);
a[tx][ty] = 0; a[v[i].x][v[i].y] = i + 1;
a[nx][ny] = nt; vis[i] = vis[a[nx][ny] - 1] = 0; //记得还原信息。
}
}
}
}
}
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int T; cin >> T; while (T--) {
cin >> n >> m >> k; ans = k;
for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j)
a[i][j] = 0; //多测要清空
v.clear(); for (int i = 1; i <= k; ++i) {
int x, y; cin >> x >> y;
v.emplace_back(node(x, y));
a[x][y] = i; vis[i - 1] = 0;
} dfs(k); cout << ans << '\n';
}
}
B
题目大意
中国移动通信集团广东有限公司深圳分公司(以下简称深圳移动
)于
在建设通信线路的过程中,信号基站的选址是一个非常关键的问题。某城市从西到东的距离为
为了保证居民的通信质量,基站的选址还需要满足
作为总工程师,您需要决定基站的数量与位置,并计算满足所有需求的最小总成本。
题解
考虑 dp。
设
答案为
考虑转移:
令
设当前要转移的状态为
- 若
, 。 - 否则,若
,那么 。 - 否则,
。
可以发现转移需要查询前缀最小和区间赋值,可以用线段树优化,复杂度
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 5, inf = 1e15;
int n, a[N], m;
int R[N];
struct node {
int l, r;
int cov, mi;
} t[N << 2];
int lson(int x) {return x << 1;}
int rson(int x) {return x << 1 | 1;}
void pushup(int x) {t[x].mi = min(t[lson(x)].mi, t[rson(x)].mi);}
void buildtree(int x, int l, int r) {
t[x].l = l; t[x].r = r; t[x].cov = -1;
if (l == r) {t[x].mi = (!l ? 0 : inf); return ;}
int mid = (l + r) >> 1;
buildtree(lson(x), l, mid);
buildtree(rson(x), mid + 1, r);
pushup(x);
}
void pushdown(int x) {
if (~t[x].cov) {
t[lson(x)].cov = t[lson(x)].mi = t[x].cov;
t[rson(x)].cov = t[rson(x)].mi = t[x].cov;
t[x].cov = -1;
}
}
void update(int x, int L, int R, int v) {
if (t[x].l >= L && t[x].r <= R) {
t[x].cov = v;
t[x].mi = v;
return ;
} int mid = (t[x].l + t[x].r) >> 1; pushdown(x);
if (L <= mid) update(lson(x), L, R, v);
if (R > mid) update(rson(x), L, R, v);
pushup(x);
}
int query(int x, int L, int R) {
if (t[x].l >= L && t[x].r <= R) return t[x].mi;
int mid = (t[x].l + t[x].r) >> 1; pushdown(x); int res = inf;
if (L <= mid) res = min(res, query(lson(x), L, R));
if (R > mid) res = min(res, query(rson(x), L, R));
return res;
}
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int T; cin >> T; while (T--) {
cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i], R[i] = 0;
cin >> m; for (int i = 1; i <= m; ++i) {
int l, r; cin >> l >> r;
R[r] = max(R[r], l);
}
buildtree(1, 0, n);
for (int i = 1; i <= n; ++i) {
update(1, i, i, min(inf, query(1, 0, i - 1) + a[i]));
if (R[i] > 0) update(1, 0, R[i] - 1, inf);
}
cout << query(1, 0, n) << '\n';
}
}
F
题面大意
有
您将要在格子中进行若干次旅行。每次旅行时,您会得到旅行的起点
当您位于格子
您的任务是依次处理
:将 修改为 。 :将 修改为 。 :给定旅行的起点 与一个颜色集合 。假设如果进行这样的一次旅行,求出取走的球的权值之和最大是多少。注意,由于我们仅仅假设进行一次旅行,因此并不会真的取走任何球。即,所有询问之间是独立的。
题解
可以发现旅行过程中一定能走就走,所以能拿到的球其实是一个区间。
考虑分别二分这个区间的左右端点。
需要判断一个区间是否由给出的颜色集合组成,队友当场写了一个 bitset,然后发现要 4 GB 空间,可以用平衡树来维护区间某种颜色的出现次数。
时间复杂度为
赛后换了一个 WBLT,不知道能不能过,代码队友是写的。
作者:CTHOOH
出处:https://www.cnblogs.com/CTHOOH/p/17741570.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具