比赛链接

A

题意

判断输入字符串与 \(\pi\) 的最长前缀匹配,不超过 \(30\) 位。

题解

知识点:模拟。

抄样例最后一个 \(30\) 都正确的,直接匹配。

时间复杂度 \(O(1)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
string s;
cin >> s;
string pi = "314159265358979323846264338327";
int cnt = 0;
for (int i = 0;i < s.size();i++) {
if (s[i] != pi[i]) break;
cnt++;
}
cout << cnt << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

B

题意

有 \(n\) 个骰子,数字 \(a_i \in [1,6]\) 的面朝上,现在给你骰子面朝上数字的总和 \(s\) ,以及去掉一个最大值的数字总和 \(r\) ,要求还原一个合法的 \(a_i\) 。

题解

知识点:枚举。

方法有很多,这里提供一种写起来很方便的。

先存一个最大值 \(mx = s - r\) ,依次给每个骰子分配点数 \(\min(mx,r)\) ,超过最大值直接给最大值。

但是为了保证不出现数字为 \(0\) 的情况,一开始先给所有骰子分配 \(1\) 点。

时间复杂度 \(O(n)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int n, s, r;
cin >> n >> s >> r;
s -= n;
r -= n - 1;
int mx = s - r;
cout << mx + 1 << ' ';
for (int i = 2;i <= n;i++) {
cout << min(r, mx) + 1 << ' ';
r -= min(r, mx);
}
cout << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

C

题意

对一个排列 \(p\) ,生成 \(n\) 个长为 \(n-1\) 新序列,第 \(i\) 个序列由 \(p\) 除了 \(p_i\) 的元素构成。

现在给你 \(p\) 生成的 \(n\) 个序列,但顺序是打乱的,要求还原一个合法的 \(p\) 。

题解

知识点:构造。

注意到 \(p_1\) 在 \(n\) 个序列的第一位会出现 \(n-1>1\) 次,而 \(p_2\) 只会出现 \(1\) 次 ,我们可以直接确定 \(p_1\) 。确定了 \(p_1\) ,我们找到没有 \(p_1\) 的一个序列,里面包含了 \(p_2,\cdots,p_n\) 直接输出即可。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[107][107];
int cnt[107];
bool solve() {
int n;
cin >> n;
int fst = 0;
vector<int> cnt(n + 1);
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n - 1;j++) {
cin >> a[i][j];
}
cnt[a[i][1]]++;
if (cnt[a[i][1]] > 1) fst = a[i][1];
}
cout << fst << ' ';
for (int i = 1;i <= n;i++) {
if (a[i][1] != fst) {
for (int j = 1;j <= n - 1;j++) cout << a[i][j] << " \n"[j == n - 1];
break;
}
}
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

D

题意

给定一组数 \(a_i\) ,要求分出最少的分组,每组要求由连续上升的正整数构成。

对于连续上升,如 \(3,4,5,6\) 是连续上升的,而 \(1,3,4,5\) 不是,因为 \(1,3\) 中间跳过了 \(2\) 。

题解

知识点:贪心,枚举。

map 记录每个数的个数,从小到大遍历,设当前数字为 \(x\) ,上一次的数字为 \(pre\) , \(cnt\) 表示数字的个数:

  1. 如果 \(x > pre + 1\) ,我们就必须开新的 \(cnt_x\) 个分组,答案加 \(cnt_x\) 。
  2. 如果 \(x = pre + 1\) ,则可以与上一次共用分组 \(cnt_x\) 个分组,但如果 \(cnt_x>cnt_{pre}\) ,那么多出来的部分要开新的分组,答案加 \(cnt_x-cnt_{pre}\) 。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int n;
cin >> n;
map<int, int> mp;
for (int i = 1;i <= n;i++) {
int x;
cin >> x;
mp[x]++;
}
int ans = 0, pre = -1;
for (auto [x, y] : mp) {
if (x > pre + 1) ans += y;
else ans += max(0, y - mp[pre]);
pre = x;
}
cout << ans << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

E

题意

给定一个正整数 \(x \leq 2^{29}\) ,找到两个正整数 \(a,b \leq 2^{32}\) ,满足 \(\dfrac{a+b}{2} = a \oplus b = x\) 。

题解

知识点:位运算,构造。

我们分析 \(x\) 的第 \(i\) 位 \(x_i = 1\) ,则 \(a,b\) 相关位置的情况,假设 \(a,b\) 一开始都为 \(0\) :

  1. 首先满足 \(a \oplus b = x\) ,显然 \(a_i,b_i\) 必须是一个 \(1\) 和一个 \(0\) ,我们不妨设 \(a_i = 1,b_i = 0\) 。

  2. 其次要满足 \(\dfrac{a+b}{2} = x\) ,根据1得到的 \(a_i+b_i = 1\) 不能满足除以 \(2\) 使得 \(x_i = 1\) ,考虑对附近其他位做调整。

    考虑调整 \(i+1\) 位,那么 \(a_{i+1},b_{i+1}\) 必须是一个 \(1\) 和一个 \(0\) ,我们不妨设 \(a_{i+1} = 1,b_{i+1} = 0\) 。但是,这样 \(x_{i+1}\) 必须等于 \(1\) ,假设 \(x_{i+1}=1\),那么对于 \(i+1\) 位的第二步因为第 \(i\) 位锁定了,只能再往 \(i+2\) 位考虑,这是没有尽头的,所以这条路走不通。

    考虑调整 \(i-1\) 位,那么就必须 \(a_{i-1} = b_{i-1} = 1\) 才能产生一个进位使得 \(x_i = 1\) 。此时, \(x_{i-1}\) 必须等于 \(0\) ,假设 \(x_{i-1} = 0\) 就恰好满足所有需求。

综上,对于任何一个 \(x_i = 1\) 要满足 \(x_{i-1} = 0\) 时才有解(特别地, \(x_1 = 1\) 时无解)。满足有解条件后,我们考虑令 \(a_i = 1,b_i = 0\) , \(a_{i-1} = b_{i-1} = 1\) ,即 \(a = 3 \cdot \dfrac{x}{2},b = \dfrac{x}{2}\) 。

时间复杂度 \(O(1)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int x;
cin >> x;
int y = x >> 1;
if ((x & 1) || (x & y)) return false;
else cout << (x | y) << ' ' << y << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

F

题意

题解

方法一

知识点:根号分治,bfs,贪心。

做法很简单,每加入一个黑点就bfs一遍附近找到最近的距离,主要在两个优化上:

  1. 用 \(f_u\) 记录距离 \(u\) 最近的黑点的距离,每次只更新 \(f_v > f_u+1\) 的情况,能有效减小bfs的范围。
  2. 用 \(ans\) 答案来限制搜索深度,因为距离大于 \(ans\) 的点没必要更新了,下一次更新 \(ans\) 一定在 \(f_u \leq ans-1\) 的情况。

证明:

在前 \(\sqrt n\) 次,一定能使得 \(ans\) 小于等于 \(2\sqrt n\) ,期间最多遍历 \(n\sqrt n\) 次。

在这之后,优化1能保证每个点只会在 \(f\) 变小时被遍历,优化2保证更新的距离不超过 \(ans \leq 2\sqrt n\) 。综上,每个点的 \(f\) 最多只会被更新 \(2\sqrt n\) 次,即每个点遍历不会超过 \(2 \sqrt n\) 次。

综上复杂度是 \(O(n\sqrt n)\)

时间复杂度 \(O(n \sqrt n)\)

空间复杂度 \(O(n)\)

方法二

知识点:根号分治,dfs,贪心。

不同于方法一的bfs遍历,这种方法设 \(f_u\) 为以 \(u\) 为根的子树中到 \(u\) 最近的黑点的距离,这样每次只需要更新 \(u\) 到根节点 \(1\) 路径上的点即可,并且保证更新层数不超过当前的 \(ans\) ,就可以做到和方法一一样的复杂度,但实际上跑的更快。

证明同方法一类似,前 \(\sqrt n\) 次能让 \(ans \leq 2\sqrt n\) ,后续每次遍历不超过 \(ans \leq 2\sqrt n\) 个点。

时间复杂度 \(O(n\sqrt n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct Graph {
struct edge {
int v, nxt;
};
int idx;
vector<int> h;
vector<edge> e; Graph(int n, int m):idx(0), h(n + 1), e(m + 1) {}
void init(int n) {
idx = 0;
h.assign(n + 1, 0);
} void add(int u, int v) {
e[++idx] = { v,h[u] };
h[u] = idx;
}
};
const int N = 200007, M = N << 1;
int c[N];
Graph g(N, M);
int f[N]; bool solve() {
int n;
cin >> n >> c[1];
for (int i = 2;i <= n;i++) cin >> c[i];
g.init(n);
for (int i = 1;i < n;i++) {
int u, v;
cin >> u >> v;
g.add(u, v);
g.add(v, u);
} for (int i = 1;i <= n;i++) f[i] = n + 1;
int ans = n + 1;
for (int i = 1;i <= n;i++) {
ans = min(ans, f[c[i]]);
f[c[i]] = 0;
queue<int> q;
q.push(c[i]);
while (!q.empty()) {
int u = q.front();
q.pop();
if (f[u] >= ans - 1) continue;
for (int j = g.h[u];j;j = g.e[j].nxt) {
int v = g.e[j].v;
if (f[v] > f[u] + 1) {
f[v] = f[u] + 1;
q.push(v);
}
}
}
if (i > 1) cout << ans << " \n"[i == n];
}
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long; struct Graph {
struct edge {
int v, nxt;
};
int idx;
vector<int> h;
vector<edge> e; Graph(int n, int m):idx(0), h(n + 1), e(m + 1) {}
void init(int n) {
idx = 0;
h.assign(n + 1, 0);
} void add(int u, int v) {
e[++idx] = { v,h[u] };
h[u] = idx;
}
};
const int N = 200007, M = N << 1;
int c[N];
Graph g(N, M);
int fa[N];
int f[N]; void dfs(int u, int fa) {
::fa[u] = fa;
for (int i = g.h[u];i;i = g.e[i].nxt) {
int v = g.e[i].v;
if (v == fa) continue;
dfs(v, u);
}
} bool solve() {
int n;
cin >> n >> c[1];
for (int i = 2;i <= n;i++) cin >> c[i];
g.init(n);
for (int i = 1;i < n;i++) {
int u, v;
cin >> u >> v;
g.add(u, v);
g.add(v, u);
}
dfs(1, 0); for (int i = 1;i <= n;i++) f[i] = n + 1;
int ans = n + 1;
for (int i = 1;i <= n;i++) {
int u = c[i], dis = 0;
while (u && dis < ans) {
ans = min(ans, dis + f[u]);
f[u] = min(f[u], dis);
dis++;
u = fa[u];
}
if (i > 1)cout << ans << " \n"[i == n];
}
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

G

题意

给一张 \(n\) 个点 \(m\) 条边的连通无向图,在图上的 \(b\) 个点会存在 bonus 标记(不可移动)。游戏开始前,会存在 \(p\) 个 token 在图点上,可以按照以下步骤移动:

  1. 开局可以随意移动一个。
  2. 若某次 token 移动到了 bonus 点,那么可以有一次移动其他 token 机会。
  3. token 可以重叠。
  4. 某次移动使得 token 到编号为 \(1\) 的点,则游戏立刻胜利。

问游戏是否会胜利。

题解

知识点:模拟,贪心,枚举。

我们从两方面考虑:

  1. 能使 token 到达 \(1\) 的路径。
  2. token 能提供多少次移动机会。

先考虑何种路径才能到达 \(1\) 。为了保证每次移动都有下次机会,那么到 \(1\) 的路径除了起点和终点 \(1\) 都必须是 bonus 点。同时,我们还需要知道移动几次才能到达 \(1\) ,方便后面比较其他点的贡献。因此,我们从 \(1\) bfs,遇到不是 bonus 的点只更新距离,不放队列扩展。

再考虑 token 能提供多少机会。在此之前,我们对 bonus 点的性质进行讨论,发现如果 bonus 点相邻另一个 bonus 点,那么进入这个 bonus 点就能提供无限次机会,而剩下一些孤儿 bonus 点,则进入这个点只会提供一次机会,因此我们先对 bonus 点记录是否无限的状态,这个可以在建图的时候就处理好。之后,我们遍历所有 token ,如果他们的邻居是无限 bonus 点则可以提供无限次机会,这里可以记为 \(n\) 次;如果他们的邻居是孤儿 bonus 点则只能提供一次,记为 \(1\) 次。

最后我们求出 token 贡献总和,枚举每个 token 点判断能否到达 \(1\) 。如果可达,则减去这个点的贡献求出其他点的总贡献,如果其他点的贡献大于等于距离减 \(1\) (开局第一次移动不需要任何 bonus )则一定可以到达。

时间复杂度 \(O(n+m)\)

空间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; const int N = 200007;
vector<int> g[N];
int st[N], dis[N], val[N]; bool solve() {
int n, m;
cin >> n >> m;
int p, b;
cin >> p >> b;
for (int u = 1;u <= n;u++) st[u] = 0, g[u].clear(), dis[u] = -1, val[u] = 0;
for (int i = 1, x;i <= p;i++) cin >> x, st[x] |= 1;
for (int i = 1, x;i <= b;i++) cin >> x, st[x] |= 2;
for (int i = 1;i <= m;i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
if ((st[u] & 2) && (st[v]) & 2) {
st[u] |= 4;
st[v] |= 4;
}
}
dis[1] = 0;
queue<int> q;
q.push(1);
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto v : g[u]) {
if (~dis[v]) continue;
dis[v] = dis[u] + 1;
if (st[v] & 2) q.push(v);
}
}
for (int u = 1;u <= n;u++) if (st[u] & 1) for (auto v : g[u]) if (st[v] & 2) { val[u] = st[v] & 4 ? n : 1;if (st[v] & 4) break; }
ll sum = 0;
for (int u = 1;u <= n;u++) if (st[u] & 1) sum += val[u];
for (int u = 1;u <= n;u++) if (st[u] & 1) if (~dis[u] && sum - val[u] >= dis[u] - 1) { cout << "YES" << '\n'; return true; }
return false;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << "NO" << '\n';
}
return 0;
}

Codeforces Round #847 (Div. 3) A-G的更多相关文章

  1. Educational Codeforces Round 47 (Div 2) (A~G)

    目录 Codeforces 1009 A.Game Shopping B.Minimum Ternary String C.Annoying Present D.Relatively Prime Gr ...

  2. Educational Codeforces Round 46 (Div 2) (A~G)

    目录 Codeforces 1000 A.Codehorses T-shirts B.Light It Up C.Covered Points Count(差分) D.Yet Another Prob ...

  3. Educational Codeforces Round 45 (Div 2) (A~G)

    目录 Codeforces 990 A.Commentary Boxes B.Micro-World C.Bracket Sequences Concatenation Problem D.Graph ...

  4. Codeforces Round #582 (Div. 3)-G. Path Queries-并查集

    Codeforces Round #582 (Div. 3)-G. Path Queries-并查集 [Problem Description] 给你一棵树,求有多少条简单路径\((u,v)\),满足 ...

  5. Codeforces Round #368 (Div. 2)

    直达–>Codeforces Round #368 (Div. 2) A Brain’s Photos 给你一个NxM的矩阵,一个字母代表一种颜色,如果有”C”,”M”,”Y”三种中任意一种就输 ...

  6. Codeforces Round #279 (Div. 2) ABCDE

    Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems     # Name     A Team Olympiad standard input/outpu ...

  7. 贪心+模拟 Codeforces Round #288 (Div. 2) C. Anya and Ghosts

    题目传送门 /* 贪心 + 模拟:首先,如果蜡烛的燃烧时间小于最少需要点燃的蜡烛数一定是-1(蜡烛是1秒点一支), num[g[i]]记录每个鬼访问时已点燃的蜡烛数,若不够,tmp为还需要的蜡烛数, ...

  8. Codeforces Round #383 (Div. 2) 题解【ABCDE】

    Codeforces Round #383 (Div. 2) A. Arpa's hard exam and Mehrdad's naive cheat 题意 求1378^n mod 10 题解 直接 ...

  9. 模拟 Codeforces Round #249 (Div. 2) C. Cardiogram

    题目地址:http://codeforces.com/contest/435/problem/C /* 题意:给一组公式,一组数据,计算得到一系列的坐标点,画出折线图:) 模拟题:蛮恶心的,不过也简单 ...

  10. Codeforces Round #368 (Div. 2) B. Bakery (模拟)

    Bakery 题目链接: http://codeforces.com/contest/707/problem/B Description Masha wants to open her own bak ...

随机推荐

  1. 每天学五分钟 Liunx 0110 | 服务篇:守护进程 systemd

    有些进程会在系统上运行较长时间,如前面的 Hello World 程序运行时产生的进程.有些进程运行瞬间就结束了,如执行 ps 命令产生的进程,也有的进程会常驻在内存中,提供相应的服务,这样的进程称为 ...

  2. Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作

    本文为博主原创,转载请注明出处: 1. 使用 ThreadPoolTaskExecutor  封装自定义配置的线程池Bean ThreadPoolTaskExecutor 是Spring 中封装的一个 ...

  3. centos7 docker配置防火墙firewalld

    docker防火墙使用的是底层iptables,封装后的firewalld默认不生效 如果想要使用firewalld,需要做以下调整: 让firewalld移除DOCKER-USER并新建一个 # R ...

  4. [转帖]记录一则enq: TX - row lock contention的分析过程

    https://www.cnblogs.com/jyzhao/p/8628184.html 故障描述:与客户沟通,初步确认故障范围大概是在上午的8:30-10:30之间,反应故障现象是Tomcat的连 ...

  5. [转帖]Linux系统awk命令详解

    AWK 是一种处理文本文件的语言,是一个强大的文本分析工具. 之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 ...

  6. MySQL新增数据,修改数据,删除数据

    连接本地mysql语句 mysql -hlocalhost -uroot -proot DML-介绍 DML英文全称是:用来对数据库中表的数据记录进行 增 删 改 操作. 增加使用 insert 删除 ...

  7. js快速获取当前时间并且返回想要的格式

    function backCurrentTime (type) { let currentTime=new Date( new Date() + 8 * 3600 * 1000 ).toJSON(). ...

  8. Git的使用(二):远程仓库

    在github上创建远程仓库 本地创建Git仓库适合自己一个人完成工程,但是实际情况中我们需要其他人来协作开发,此时就可以把本地仓库同步到远程仓库,同时还增加了本地仓库的一个备份.常用的远程仓库就是g ...

  9. Map结构映射,避免每一个字段赋值

    var query1 = (from fore in forecastShippingDate join ship in shipOutOfStock on fore.Id equals ship.F ...

  10. 大语言模型的预训练[5]:语境学习、上下文学习In-Context Learning:精调LLM、Prompt设计和打分函数设计以及ICL底层机制等原理详解

    大语言模型的预训练[5]:语境学习.上下文学习In-Context Learning:精调LLM.Prompt设计和打分函数(Scoring Function)设计以及ICL底层机制等原理详解 1.I ...