2024“钉耙编程”中国大学生算法设计超级联赛(8)

news/2024/11/13 9:04:47/文章来源:https://www.cnblogs.com/luckyblock/p/18356024

目录
  • 写在前面
  • 1004
  • 1007
  • 1012
  • 1006
  • 1005
  • 1008
  • 1003
  • 1010
  • 写在最后

写在前面

补提地址:https://acm.hdu.edu.cn/listproblem.php?vol=66,题号 7517~7528。

以下按个人向难度排序。

dztlb 大神回去和npy约会了,于是悲惨单刷。

最后 6 题好歹签到是都签完了不算太烂,唉一个人单刷前期看到大家飞速过完好多题自己签到都签不上实在红温!

1004

签到。

发现操作后一定会移动到最外一圈的格子上,且之后只能在最外圈的格子上移动,则这些格子的贡献一定都能取到。

然后考虑能否获得其他格子的贡献,发现可以通过在开始时进行反复横跳,从而多获得某一行/某一列的贡献。

特判下即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {//freopen("1.txt", "r", stdin);std::ios::sync_with_stdio(0), std::cin.tie(0);int T; std::cin >> T;while (T --) {LL n, m, a, b; std::cin >> n >> m >> a >> b;LL ans = 0;if (n == 1 || m == 1) ans = n * m;else ans = 2 * n + 2 * m - 4;if ((a == 1 && b == 1) || (a == 1 && b == 1) || (a == n && b == 1) || (a == n && b == m)) {//hina daisuki!!!} else if (a == 1 || a == n) {ans += (n - 2);} else if (b == 1 || b == m) {ans += (m - 2);} else {ans += std::max(n - 2, m - 2);}std::cout << ans << "\n";}return 0;
}

1007

进制,数论。

先考虑何时会出现答案无限的情况,考虑 \(k\) 极大的情况可知,这是因为在任意进制下 \(a+b\) 均不发生进位导致的。此时一定有 \(a+b = c\),特判下即可。

则某个进制 \(k\) 对答案有贡献当且仅当此时出现了进位,且减去进位 \(d\) 后有 \(a+b - d = c\)。众所周知,异或作为二进制不进位加法是有个性质对应的:

\[a \oplus b = (a \operatorname{or} b) - (a \operatorname{and} b) \]

考虑对于此题扩展一下该性质。发现若 \(k\) 进制下某一位上出现了 \(a_i+b_i\) 的进位,则一定有:

\[c_i = a_i + b_i - k \]

否则有:

\[c_i = a_i + b_i \]

则对于某一位上进位的情况,移项可知 \(k = a_i + b_i - c_i\),否则有 \(a_i + b_i - c_i = 0\)。则可知在 \(k\) 进制下,\(a+b-c\) 一定可以表示成若干 \(k\) 的幂的和(即发生的进位之和),即有:

\[\begin{aligned}a + b - c &= k^{c_1} + k^{c_2} + \cdots + k^{c_m} &(1\le c_1<c_2 <\cdots)\\&=k(k^{c_1 - 1} + k^{c_2 - 1} + \cdots + k^{c_m - 1}) \end{aligned}\]

则可知若某个进制 \(k\) 是对答案有贡献的,则 \(k\) 一定是 \(a+b-c\) 的因数。有 \(a,b,c\le 10^9\),则可以直接大力枚举 \(a+b-c\) 的所有因数,并大力枚举各位检查。当 \(a+b-c = 0\) 也即不发生进位时无解。

总时间复杂度 \(O(T\sqrt{v}\log v)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
LL a, b, c, d;
//=============================================================
bool check(LL k_) {LL x = a, y = b, z = c;while (x || y || z) {LL t1 = x % k_, t2 = y % k_, t3 = z % k_;if ((t1 + t2) % k_ != t3) return false;x /= k_, y /= k_, z /= k_;}return true;
}
//=============================================================
int main() {//freopen("1.txt", "r", stdin);std::ios::sync_with_stdio(0), std::cin.tie(0);int T; std::cin >> T;while (T --) {std::cin >> a >> b >> c;d = a + b - c;if (d == 0) {std::cout << -1 << "\n";continue;}LL ans = 0;if (check(d)) ++ ans;for (LL i = 2; i * i <= d; ++ i) {if (d % i != 0) continue;if (check(i)) ++ ans;if (i * i != d && check(d / i)) ++ ans;}std::cout << ans << "\n";}return 0;
}

1012

思维。

这题是我倒数第二个过的题哈哈单刷签到失败是这样的

1006

最小生成树,枚举

实际大力题呃呃,赛时被骗了最后半小时才恍然大悟实在唐得一批

考虑能否在直接枚举所有边做 Kruscal 时,一次性地把所有轮的最小生成树全做出来。即考虑对于每次枚举到一条边 \((u, v)\) 时,找到这条边应当加到哪一轮做的最小生成树上,以求得这条边对应答案。

发现若某条边 \((u, v)\) 应当被加到第 \(i\) 轮上,说明在第 \(1\sim i - 1\) 轮中 \(u, v\) 两节点均已经联通,且在 \(i+1\sim \cdots\) 轮中两节点均不联通,发现存在单调关系,于是考虑维护每轮中各节点的连通性,并二分检查各轮中两节点的连通性即可求得其被加入的轮数。

又发现由于最多只会做 \(\frac{m}{n-1}\) 轮,且仅有 \(n\) 个节点,完全可以直接开 \(\frac{m}{n-1}\) 个大小为 \(n\) 的并查集来维护连通性,时空复杂度均摊均仅有 \(O(m)\) 级别。

在做最小生成树的过程中再顺便维护下对于每一轮已经加了几条边,最后再枚举每条边,若这条边对应的轮数上有 \(n-1\) 条边说明这一轮被做完了即可直接输出,否则输出 -1

精细实现并查集总时间复杂度能做到纯 \(O(m)\) 级别,懒狗可以像我一样直接大力上 map,总时间复杂度 \(O(m\log m)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pr std::pair
#define mp std::make_pair
const int kN = 1e5 + 10;
const int kM = 3e5 + 10;
//=============================================================
int n, m, u[kM], v[kM], ans[kM];
int cnt[kM];
std::map<int, int> fa[kM];
//=============================================================
int find(int id_, int x_) {if (!fa[id_].count(x_)) fa[id_][x_] = x_;return (x_ == fa[id_][x_]) ? x_ : (fa[id_][x_] = find(id_, fa[id_][x_]));
}
void merge(int id_, int x_, int y_) {int fx = find(id_, x_), fy = find(id_, y_);fa[id_][fx] = fy;
}
//=============================================================
int main() {// freopen("1.txt", "r", stdin);std::ios::sync_with_stdio(0), std::cin.tie(0);int T; std::cin >> T;while (T --) {std::cin >> n >> m;for (int i = 1; i <= m; ++ i) ans[i] = -1, cnt[i] = 0, fa[i].clear();for (int i = 1; i <= m; ++ i) {int u_, v_; std::cin >> u_ >> v_;u[i] = u_, v[i] = v_;ans[i] = -1;}int maxround = 0;for (int i = 1; i <= m; ++ i) {int u_ = u[i], v_ = v[i], nowround = maxround + 1;for (int l = 1, r = maxround; l <= r; ) {int mid = (l + r) >> 1;if (cnt[mid] >= n - 1 || find(mid, u_) == find(mid, v_)) {l = mid + 1;} else {nowround = mid;r = mid - 1;}}maxround = std::max(maxround, nowround);ans[i] = nowround;++ cnt[nowround];merge(nowround, u_, v_);}for (int i = 1; i <= m; ++ i) {if (cnt[ans[i]] < n - 1) ans[i] = -1;std::cout << ans[i] << " ";}std::cout << "\n";}return 0;
}

1005

DP,记忆化搜索

大概是记忆化搜索题?

先特判下 \(k\ge 60\)\(n\) 可在区间内任意取,答案即 \(r-l+1\)

然后一个很显然的想法是直接递归求解。记 \(f(l, r, k)\) 表示对区间 \([l, r]\) 进行二分且允许出现 \(k\) 次越界情况下,区间内 \(n\) 的合法取值个数。考虑在这一轮上检查 \(\operatorname{mid}\) 的结果,则有:

\[f(l, r, k) = 1 + f(l, \operatorname{mid} - 1, k - 1) + f(\operatorname{mid} + 1, r, k) \]

然后这个时候大家写完过了样例看着挺优美的就会想直接冲一发交上发现 TLE 了哈哈,然后才发现这东西在 \(k=59\) 时实际上会把整个 \([l,r]\) 全部遍历一遍,实际是非常丑的东西。

然后又发现实际上 \(f(l, r, k)\) 的答案与区间的具体位置是无关的,实际仅与区间长度 \(\operatorname{len}=r-l+1\) 有关。于是考虑在上述递归过程中记忆化 \(g(\operatorname{len},k)\) 表示长度为 \(\operatorname{len}\) 的区间允许 \(k\) 次越界的答案即可。

可以证明在 \(k\) 次二分后二分的区间长度至多仅有 2 种,精细实现时间复杂度能做到 \(O(T\log^2 (r - l + 1))\) 级别,懒狗还可以像我一样直接大力上 map,总时间复杂度 \(O(T\log^3 (r - l + 1))\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pr std::pair
#define mp std::make_pair
//=============================================================
LL l, r, k, ans;
std::map<pr<LL, int>, LL> cnt;
//=============================================================
LL check(LL l_, LL r_, LL c_) {if (l_ > r_) return 0;if (c_ < 0) return 0;if (cnt.count(mp(r_ - l_ + 1, c_))) return cnt[mp(r_ - l_ + 1, c_)];LL mid = (l_ + r_) / 2ll, delta = 1;delta += check(l_, mid - 1, c_ - 1);delta += check(mid + 1, r_, c_);cnt[mp(r_ - l_ + 1, c_)] = delta;return delta;
}
//=============================================================
int main() {// freopen("1.txt", "r", stdin);std::ios::sync_with_stdio(0), std::cin.tie(0);int T; std::cin >> T;while (T --) {std::cin >> l >> r >> k;if (k >= 60) {std::cout << (r - l + 1) << "\n";continue;}cnt.clear();ans = check(l, r, k);std::cout << ans << "\n";}return 0;
}
/*
1
1 100 0
*/

1008

构造。

好玩构造,最人类智慧的一集。

显然数列 \(a, b\) 均可以构造成 \(1\sim n\) 的排列的形式,若不是排列仅需离散化,即可使其保持性质不变的同时变为排列。

我的做法是考虑把每个节点的 \((a, b)\) 当做坐标,以 \(a\) 为横坐标 \(b\) 为纵坐标扔到二维坐标系上考虑。那么对于一个大小为 7 的完全二叉树的样例可以画成这个形式:

输入:

1
7
1 1 2 2 3 3

输出:

7 7 3 6 6 3 1 5 2 4 4 2 5 1

可以发现有如下性质:

  1. 根节点坐标一定为 \((n, n)\)
  2. 节点 \(u\) 的子树中所有节点,一定全部位于该节点坐标 \((a_u, b_u)\) 的左下角。
  3. 对于节点 \(u\) 的不同的子节点 \(v', v''\),若有 \(a_{v'} < a_{v''}\),则为了维持性质一定有 \(b_{v'} > b_{v''}\)

有矩阵的包含关系,于是考虑从根开始递归构造:

  1. 由于最终答案里 \(a\) 在前面,则应当首先令编号较小的节点的 \(a\) 尽可能小,于是考虑优先向子树内编号最小的节点更小的子节点递归构造,并尝试最小化子树的所有点 \(a\) 的值。
  2. 最优情况下应当构造成节点 \(u\) 满足:\(a_u\) 为其子节点 \(a\) 最大值+1, \(b_u\)为其子节点 \(b\) 最大值+1。
  3. 由上述性质 3,在最小化子树内编号最小的节点更小的子节点的 \(a\) 的同时,其子树内的 \(b\) 应当是更大的,且由构造 2 可知子树内编号最小的节点最小的子节点 \(v\)\(b_v=b_{u} - 1\)
  4. 可知递归构造时,对于某个子树内节点可以很容易维护其 \(a\)下界 \(A\)\(b\)上界 \(B\)
  5. 对于两个遍历顺序相邻的子节点 \(v', v''\),有下界 \(A_{v''} = a_{v'} + 1\),上界 \(B_{v'} = b_{v''} - \operatorname{size}_{v'}\)

由上述性质,发现仅需预处理一下子树大小 \(\operatorname{size}\) 和子树内最小编号节点的编号 \(\operatorname{id}\),对每个节点子节点按照 \(\operatorname{id}\) 排个序后递归构造。递归时下传每个节点内子树的 \(a\) 的下界和 \(b\) 的上界,递归时直接计算 \(b\),回溯时求得每个节点 \(a\) 的值即可。总时间复杂度 \(O(n)\) 级别。

发现钦定了递归构造的顺序即 dfs 序。虽然上述构造方法有点麻烦但实际上述构造方法和题解本质是完全一样的,属实是殊途同归了!

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN], b[kN], sz[kN], id[kN];
std::vector<int> son[kN];
//=============================================================
void dfs1(int u_) {sz[u_] = 1;id[u_] = u_;for (auto v_: son[u_]) {dfs1(v_);sz[u_] += sz[v_];id[u_] = std::min(id[u_], id[v_]);}
}
void dfs2(int u_, int a_, int b_) {a[u_] = a_, b[u_] = b_;-- b_;for (auto v_: son[u_]) {dfs2(v_, a_, b_);b_ = b[v_] - sz[v_];a_ = a[v_] + 1;a[u_] = std::max(a[u_], a[v_] + 1);}
}
bool cmp(int fir_, int sec_) {return id[fir_] < id[sec_];
}
//=============================================================
int main() {//freopen("1.txt", "r", stdin);std::ios::sync_with_stdio(0), std::cin.tie(0);int T; std::cin >> T;while (T --) {std::cin >> n;for (int i = 1; i <= n; ++ i) son[i].clear();for (int i = 2; i <= n; ++ i) {int fa; std::cin >> fa;son[fa].push_back(i);}dfs1(1);for (int i = 1; i <= n; ++ i) std::sort(son[i].begin(), son[i].end(), cmp);dfs2(1, 1, sz[1]);for (int i = 1; i <= n; ++ i) std::cout << a[i] << " " << b[i] << " ";std::cout << "\n";}return 0;
}
/*
1
7 
1 1 2 2 3 3
*/

1003

DP,期望。

1010

01 Trie,数据结构。

我测好牛逼的标记合并!

写在最后

学到了什么:

  • 1008:二维偏序关系扔到坐标系上考虑。
  • 1010:一种对于位运算的懒标记合并。

昨天是花冈柚子的生日,把这条信息转发至三个群电脑就会自动下载柚子社全家桶。我试过了是假的,而且我的电脑自动下载了原神,但昨天真的是花冈柚子的生日。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/783046.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文献阅读——Dynamic Range Compression Preserving Local Image Contrast for Digital Video Camera

Dynamic Range Compression Preserving Local Image Contrast for Digital Video Camera 数字摄像机中保持局部图像对比度的动态范围压缩 摘要 本文提出了一种新的数字摄像机动态范围压缩方法。该算法的目的是在像素和周围注意力区域的亮度比来保持局部对比度。通常,数字视频摄…

Dynamic Range Compression Preserving Local Image Contrast for Digital Video Camera

Dynamic Range Compression Preserving Local Image Contrast for Digital Video Camera 数字摄像机中保持局部图像对比度的动态范围压缩 摘要 本文提出了一种新的数字摄像机动态范围压缩方法。该算法的目的是在像素和周围注意力区域的亮度比来保持局部对比度。通常,数字视频摄…

基于pi控制的数字锁相环simulink建模与仿真

1.算法运行效果图预览 (完整程序运行后无水印)2.算法运行软件版本 matlab2022a3.部分核心程序 (完整版代码包含详细中文注释和操作步骤视频)01_012m4.算法理论概述数字锁相环(DPLL, Digital Phase Locked Loop)是一种重要的电路系统,广泛应用于通信、信号处理、频率合成等领…

Kubernetes 存储架构及插件使用

目录本文转载本文转载自阿里巴巴云原生微信公众号(ID:Alicloudnative)。一、Kubernetes 存储体系架构1、引例: 在 Kubernetes 中挂载一个 Volume2、Kubernetes 的存储架构3、PV Controller3.1、基本概念3.2、PV 的状态迁移图3.3、PVC 的状态迁移图3.4、PVC 绑定 PV 流程3.5…

BC变换的EWMA控制图对顺丰控股股票的分析研究

全文链接:https://tecdat.cn/?p=37326 原文出处:拓端数据部落公众号 量化投资可以带来不菲的回报,但缺乏丰富的知识却难以实现。统计质量管理的思想通俗易懂,且对于市场相对稳定的股市有监控作用。结合历史股票数据和统计质量管理的思想,对数据作合适的处理并设计合理的控…

微信小程序目录结构

一、小程序框架 微信开放平台——小程序框架介绍 小程序的目录结构很清晰,主要由描述整体内容的app和描述具体页面的page组成。一般来说,习惯对小程序的目录结构进行更加清晰的规划,例如将程序种会用到的图片统一用imgs文件夹进行管理、pages文件夹下面就是不同页面对于的子…

【架构师视角系列】风控场景下的配置中心设计思考

风控场景通常需要频繁修改策略进行攻防对抗,一般策略管理平台与策略执行引擎是两个服务,目的是为了解耦,使得业务需求的变更对策略执行引擎执行的影响最小化。通常策略引擎获取策略配置的方法有以下几种,分别是:共享存储、远程调用或配置中心。声明 原创文章,转载请标注。…

[图文直播]使用EasyOCR识别图片上的文字

安装EasyOCRC:\Users\Administrator>pip install easyocr Collecting easyocrDownloading easyocr-1.7.1-py3-none-any.whl.metadata (11 kB) Collecting torch (from easyocr)Downloading torch-2.4.0-cp312-cp312-win_amd64.whl.metadata (27 kB) Collecting torchvision&…

内心与相关构型

内心1、三条角平分线 2、在 \(\odot M\) 上(鸡爪圆上) 3、\(AI\cdot IM=AM\cdot IK=2Rr\) ,即 \(OI^2=R^2-2Rr\)4、\(\odot I\) 与 \(\odot I_A\) 关于点 \(A\) 位似,所以 \(D\) 的对径点 \(D\) 满足 \(ADX\) 共线(两个圆过这两个点的切线平行),同理 \(AD\) 过 \(X\) 的…

【Windows系列】网卡1访问外网,网卡2访问内网!

背景 一、实验环境准备二、查看ipv4服务是否勾选和开启三、修改网卡路由四、修改网卡路由背景 当我们的Windows电脑有双网卡,若这时想要实现一张网卡用于访问外网,另一张网卡用于访问内网的功能。比如通过远程电脑,然后再通过电脑去访问我们家里的NAS存储等。通常这种需求下…

事件驱动系统设计之将事件检索与事件处理解耦

0 前言 part1讨论了集成过程中遇到的挑战以及幂等事件处理的作用。解决集成问题之后,我们需要反思事件检索的问题。我们的经验教训表明,将事件检索与事件处理解耦至关重要。 1 事件处理与请求/响应 API 紧耦合 part1讨论了将请求/响应 API 集成到事件驱动微服务中时,由于基于…

豆瓣影评数据抓取

豆瓣影评数据抓取 创建时间:2024-08-12 抓取豆瓣影评相关数据的代码,包括封面、标题、评论内容以及影评详情页的数据。 一、完整代码https://movie.douban.com/review/best/ 抓取封面 标题 评论內容 抓取完整的评论内容 也就是点击展开后的完整的 抓取当前影评的详情页的数…