2025“钉耙编程”中国大学生算法设计春季联赛(3)部分题解

news/2025/3/22 16:44:24/文章来源:https://www.cnblogs.com/lwiwi/p/18786801
  • 1001 数列计数

一个结论:\(C_{n}^{m}\)为奇数当且仅当\(n & k==k\)
于是就可以分类讨论一下,当\(l_{i}\)大于等于\(a_{i}\),此时\([0,a_{i}]\)全都可以取到,假定二进制下\(a_{i}\)\(k\)个1,方案数就是\(2^{k}\)

否则,我们就需要统计出\([0,l_{i}]\)中的,二进制下出现了1的位置都是\(a_{i}\)二进制下1的位置的数,我用的是数位dp。设\(dp[i][0/1][0/1]\)表示从高到低第\(i\)位取\(0/1\)得情况下,当前数是否等于\(l_{i}\),由于\(a_{i}\)在某一位是0时,那么这一位就不能取1,于是我们可以推出状态转移方程(详见代码

点击查看代码
#include<bits/stdc++.h>using namespace std;const long long mod = 998244353;
long long t;
const long long N = 2e5 + 10;
long long n,a[N],b[N];long long quickMul(long long x,long long k) {if(k == 0) return 1;long long tmp = quickMul(x,k / 2);tmp = (tmp * tmp) % mod;if(k & 1) tmp = (tmp * x) % mod;return tmp;
}void solve() {cin >> n;for(long long i = 1;i <= n;i++) cin >> a[i];for(long long i = 1;i <= n;i++) cin >> b[i];long long ans = 1;for(long long i = 1;i <= n;i++) {long long tmp = 0;if(a[i] <= b[i]) {tmp = a[i];ans = (ans * (quickMul(2,__builtin_popcount(tmp)))) % mod;}else {vector<long long> digit1,digit2;long long dp[110][2][2];long long temp = a[i],temb = b[i];while(temp) {digit1.push_back(temp % 2);digit2.push_back(temb % 2);temp /= 2;temb /= 2;}long long len = digit1.size() - 1;memset(dp,0,sizeof dp);if(digit2[len] == 1) {dp[len][1][1] = 1;dp[len][0][0] = 1;}else dp[len][0][1] = 1;for(long long i = len - 1;i >= 0;i--) {if(digit1[i] == 1) {if(digit2[i] == 1) {dp[i][1][1] = (dp[i + 1][1][1] + dp[i + 1][0][1]) % mod;dp[i][1][0] = (dp[i + 1][0][0] + dp[i + 1][1][0]) % mod;dp[i][0][0] = (dp[i + 1][1][1] + dp[i + 1][0][0] + dp[i + 1][0][1] + dp[i + 1][1][0]) % mod;}else {dp[i][0][1] = (dp[i + 1][1][1] + dp[i + 1][0][1]) % mod;dp[i][1][0] = (dp[i + 1][1][0] + dp[i + 1][0][0]) % mod;dp[i][0][0] = (dp[i + 1][1][0] + dp[i + 1][0][0]) % mod;}}else {if(digit2[i] == 1)dp[i][0][0] = (dp[i + 1][1][1] + dp[i + 1][0][0] + dp[i + 1][0][1] + dp[i + 1][1][0]) % mod;else {dp[i][0][1] = (dp[i + 1][1][1] + dp[i + 1][0][1]) % mod;dp[i][0][0] = (dp[i + 1][1][0] + dp[i + 1][0][0]) % mod;}}}tmp = (dp[0][0][0] + dp[0][0][1] + dp[0][1][1] + dp[0][1][0]) % mod;ans = (ans * tmp) % mod;}}cout << ans << '\n';
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t;while(t--) solve();return 0;
}
  • 1003 拼尽全力

(拼尽全力无法战胜
我们可以直接对于每一项能力建一个优先队列,那么只需要不断的重复一下过程:当前这一项能力能通过的就通过,并且在对应\(i\)公司的统计数组\(vis[i]\)加上1,表示这个公司可以在某一顺序下能力被达到\(vis[i]\)项,如果\(vis[i]\)为m,表示这个公司面试可以通过,那么就给每个能力值都加上提升的值。一直重复到没有面试可以通过,最后只需检查是不是所有的公司能达到的项数都是m即可。

点击查看代码
#include<bits/stdc++.h>using namespace std;long long t;
const long long N = 1e6+ 10;
long long n,m,vis[N];
struct node {long long val,pos;bool operator < (const node &a) const {return val > a.val;}
};
priority_queue<node> q[N];
long long a[N];void solve() {for(long long i = 0;i <= max(n,m);i++) {while(!q[i].empty()) q[i].pop();vis[i] = 0;}cin >> n >> m;vector<vector<long long>> c(n + 1,vector<long long>(m + 1));vector<vector<long long>> w(n + 1,vector<long long>(m + 1));for(long long i = 1;i <= m;i++) cin >> a[i];for(long long i = 1;i <= n;i++) {for(long long j = 1;j <= m;j++) cin >> c[i][j],q[j].push({c[i][j],i});for(long long j = 1;j <= m;j++) cin >> w[i][j];}bool flag;while(1) {flag = false;for(long long j = 1;j <= m;j++) {while(!q[j].empty() && q[j].top().val <= a[j]) {flag = true;vis[q[j].top().pos]++;if(vis[q[j].top().pos] == m) {for(long long i = 1;i <= m;i++)a[i] += w[q[j].top().pos][i];}q[j].pop();}}if(!flag) break;}flag = false;for(long long i = 1;i <= n;i++) if(vis[i] != m) flag = true;cout << (flag == false ? "YES\n" : "NO\n");
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t;while(t--) solve();return 0;
}
  • 1004 弯曲筷子

贪心着想,一根筷子需要跟长度和它最接近的筷子一起才能让不舒适度最小,所以我们可以先让所给序列排序。接着考虑dp,设dp[i][0/1][0/1]表示当前位于第i根筷子,当前筷子取/不取的情况下,目前所取筷子总数是否是奇数的最小不舒适度。若当前不取,那么前一根筷子若取了就变成奇数的情况下就不能算,因为若上一根筷子取了并且让取的筷子数量变为奇数,那么由于目前这根筷子长度与它最接近,又当前筷子不取,所以得到的结果一定不是最优。这样我们就可以写出状态转移方程(详见代码)。然而有一种特殊情况,即对于两根一定要取得筷子,有可能直接取他们两个最优而不是通过取别的来分别与他们配对(例如1 3 7 100,要求1 7必取,那么肯定是直接取这两根筷子最优),所以我们只需要记录下上一根必须取得筷子所在位置,当遇到下一个必须取得筷子时,和这种特殊情况取最优即可

点击查看代码
#include<bits/stdc++.h>using namespace std;long long t;
const long long N = 3e5 + 10;
long long n,m,p[N];
struct node {long long val,pos;bool operator < (const node &a) const {return val < a.val;}
}in[N];void solve() {for(long long i = 0;i <= n;i++) p[i] = in[i].val = in[i].pos = 0;cin >> n >> m;for(long long i = 1;i <= n;i++) cin >> in[i].val,in[i].pos = i;for(long long v,i = 1;i <= m;i++) cin >> v,p[v] = 1;sort(in + 1,in + 1 + n);long long dp[n + 1][2][2];for(long long i = 0;i <= n;i++)dp[i][0][0] = dp[i][0][1] = dp[i][1][0] = dp[i][1][1] = 9e18;dp[0][0][0] = 0;long long pre = -1;for(long long i = 1;i <= n;i++) {dp[i][1][1] = min(dp[i - 1][0][0],dp[i - 1][1][0]);if(i > 1) dp[i][1][0] = dp[i - 1][1][1] + (in[i].val - in[i - 1].val) * (in[i].val - in[i - 1].val);if(!p[in[i].pos]) dp[i][0][0] = min(dp[i - 1][0][0],dp[i - 1][1][0]);if(p[in[i].pos]) {if(~pre) dp[i][1][0] = min(dp[i][1][0],dp[pre][1][1] + (in[i].val - in[pre].val) * (in[i].val - in[pre].val));pre = i;}}cout << min(dp[n][1][0],dp[n][0][0]) << '\n';
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t;while(t--) solve();return 0;
}
  • 1005 修复公路

签到题,判连通块或者用并查集都可以,就不放代码了

  • 1007 宝石商店

可持久化trie树。与主席树类似的,我们可以在每新加一个树的时候,在仅仅需要修改的那一条路径上新加点从而达到可持久化的目的。价值的计算方式其实就是异或。然后求两个数最大异或只需要在trie树上贪心的走与当前数当前位不同的方向即可

点击查看代码
#include<bits/stdc++.h>using namespace std;int t;
const int N = 2e6 + 10;
int n,q,root[N << 5],tot;
int a[N];
int ch[N << 5][2],val[N << 5];void insert(int p,int k) {int rt = ++tot;for(int i = 31;i >= 0;i--) {val[rt] = val[p] + 1;int c = (k & (1ll << i) ? 1 : 0);if(!ch[rt][c]) ch[rt][c] = ++tot;ch[rt][!c] = ch[p][!c];rt = ch[rt][c];p = ch[p][c];}val[rt] = val[p] + 1;
}int query(int u,int p,int x) {int res = 0;for(int i = 31;i >= 0;i--) {int c = (x & (1ll << i) ? 1 : 0);if(val[ch[u][!c]] - val[ch[p][!c]] > 0) {u = ch[u][!c];p = ch[p][!c];res += (1ll << i);}else {u = ch[u][c];p = ch[p][c];}}return res;
}void solve() {for(int i = 0;i <= tot;i++) val[i] = ch[i][0] = ch[i][1] = 0;tot = 0;cin >> n >> q;for(int i = 1;i <= n;i++) {cin >> a[i];root[i] = tot + 1;insert(root[i - 1],a[i]);}for(int l,r,x,i = 1;i <= q;i++) {cin >> l >> r >> x;cout << query(root[r],root[l - 1],x) << '\n';}
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t;while(t--) solve();return 0;
}
  • 1009 部落冲突

并查集操作。与传统的并查集不同的是,该题多了让某个人转移和两部落交换的操作。鉴于后者,我们当然不能直接遍历每个人然后把他移到另一个部落,所以考虑将部落编号交换,于是可以用双向映射,即建两个数组pos[i]和mp[i],并引入一个中间值,假设最开始每个部落i都在i位置上,那么pos[i]表示此时i位置对应的部落是pos[i],而mp[i]则表示部落i此时所在的位置是mp[i]。而对于fat[i]所指向的就应该是“位置”而非“部落”。这里用扩展域并查集,1-n范围表示野蛮人所在的位置,n+1-2n所表示的是位置的fat
对于操作1,我们只需让位置b的fat指向位置a
对于操作2,我们只需让野蛮人a的fat指向位置b
对于操作3,我们只需让\(pos[mp[a+n]]\)\(pos[mp[b+n]]\)交换,再把\(mp[a+n]\)\(mp[b+n]\)交换即可
对于操作4,直接输出查询答案

点击查看代码
#include<bits/stdc++.h>using namespace std;int t;
const int N = 4e6 + 10;
int fat[N],pos[N],mp[N];
int n,q;int find(int x) {if(fat[x] != x) fat[x] = find(fat[x]);return fat[x];
}void solve() {for(int i = 0;i <= 2 * n;i++) fat[i] = mp[i] = pos[i] = 0;cin >> n >> q;for(int i = 1;i <= 2 * n;i++) {if(i <= n) fat[i] = i + n;else fat[i] = i;}for(int i = 1 + n;i <= 2 * n;i++) pos[i] = i,mp[i] = i;for(int pd,a,b,i = 1;i <= q;i++) {cin >> pd;if(pd == 1) {cin >> a >> b;fat[mp[b + n]] = fat[mp[a + n]];}else if(pd == 2) {cin >> a >> b;fat[a] = mp[b + n];}else if(pd == 3) {cin >> a >> b;swap(pos[mp[a + n]],pos[mp[b + n]]);swap(mp[a + n],mp[b + n]);}else {cin >> a;cout << pos[find(a)] - n << '\n';}}
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t;while(t--) solve();return 0;
}
  • 1010 选择配送

求最大曼哈顿距离最小值,从曼哈顿距离的式子推导:

$ \left | x - a_{i}\right |+\left | y-b_{i}\right |$

可以分成两种情况:

$ \left | (x+y) - (a_{i}+b_{i})\right |$

或者
$ \left | (x-y) - (a_{i}-b_{i})\right |$

于是对于所有的客户,我们只需要维护四个值min1,max1,min2,max2分别表示\((x_{i}+y_{i})\)的最小/最大和\((x_{i}-y_{i})\)的最小/最大,然后再遍历所有的配送站,求出最大距离的最小值即可

点击查看代码
#include<bits/stdc++.h>using namespace std;long long t;
const long long N = 1e6 + 10;
long long n,m;
struct node {long long x,y;
}in[N],st[N];void solve() {cin >> n >> m;long long min1 = 1e18,min2 = 1e18,max1 = -1e18,max2 = -1e18;for(long long i = 1;i <= n;i++) {cin >> in[i].x >> in[i].y;min1 = min(min1,in[i].x + in[i].y);min2 = min(min2,in[i].x - in[i].y);max1 = max(max1,in[i].x + in[i].y);max2 = max(max2,in[i].x - in[i].y);}long long ans = 1e18;for(long long i = 1;i <= m;i++) {cin >> st[i].x >> st[i].y;ans = min(ans,max({abs(st[i].x + st[i].y - min1),abs(st[i].x - st[i].y - min2),abs(st[i].x + st[i].y - max1),abs(st[i].x - st[i].y - max2)}));}cout << ans << '\n';
}signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> t;while(t--) solve();return 0;
}

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

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

相关文章

20244210 实验一《Python程序设计》实验报告

20244210 2024-2025-2 《Python程序设计》实验一报告 课程:《Python程序设计》 班级:2442 姓名: 陈可 学号:20244210 实验教师:王志强 实验日期:2024年3月18日 必修/选修: 公选课 1.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能; 3.编写程序,练习变…

Linux命令:top

Linux命令:top 简介 Linux top 是一个在 Linux 和其他类 Unix 系统上常用的实时系统监控工具。它提供了一个动态的、交互式的实时视图,显示系统的整体性能信息以及正在运行的进程的相关信息。 语法 top [-] [d delay] [q] [c] [S] [s] [i] [n] [b]参数说明:-d <秒数>:…

一次Java后端服务间歇性响应慢的问题排查记录

分享一个之前在公司内其它团队找到帮忙排查的一个后端服务连接超时问题,问题的表现是服务部署到线上后出现间歇性请求响应非常慢(大于10s),但是后端业务分析业务日志时却没有发现慢请求,另外由于服务容器`livenessProbe`也出现超时,导致容器出现间歇性重启。分享一个之前…

作业3:结对项目

这个作业属于哪个课程 软工23级这个作业要求在哪里 作业要求链接这个作业的目标 合作实现一个自动生成小学四则运算题目的命令行程序项目成员 陈日强 3123004736, 李铭恩 3123004745Github仓库链接 Math-Quiz-Generator一、PSP表格PSP2.1 阶段 任务 预估耗时(分钟) 实际耗时(…

鸿蒙NEXT开发案例:程序员计算器

【环境准备】 • 操作系统:Windows 10 • 开发工具:DevEco Studio 5.0.1 Release Build Version: 5.0.5.306 • 目标设备:华为Mate60 Pro • 开发语言:ArkTS • 框架:ArkUI • API版本:API 13 【项目背景与价值】 在程序员日常开发中,常常需要进行复杂的数学运算(如三角…

自定义异常--java进阶day08

1.自定义异常2.自定义异常的格式 看你想要定义哪种异常,对应的继承哪种异常类以我们之前写的代码举例,Exception类过于庞大,所有的异常子类都可以被它接收,这样就会导致无法精确捕获,所以我们要自定义异常我们自定义一个学生年龄异常定义了自己写的异常后,setAge里面抛出…

boolean io.swagger.v3.oas.models.media.Schema.getExampleSetFlag()

java17 <springdoc.version>2.3.0</springdoc.version> 错误信息 jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoSuchMethodError: boolean io.swagger.v3.oas.models.media.Schema.getExampleSetFlag() 确保项目中使用的 swagger-cor…

异常的两种处理方式--java进阶day08

1.异常的默认处理流程 java中,对于异常的默认处理方式是--向上抛出 之前我们说过,异常都是类,当某个程序出错后,就会自动生成该异常对象,而这个异常对象就如同一颗雷.java的异常默认处理方式--向上抛出,其中上,就是指上一级,调用该程序的那个逻辑 所以,在错误代码那一…

在centOS上更新yum资源报错:Cannot find a valid baseurl for repo: base/7/x86_64

在centOS中更新yum 的资源和新的依赖时,报:Cannot find a valid baseurl for repo: base/7/x86_64 报错内容:[root@localhost ~]# yum update 已加载插件:fastestmirror Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo…

并行计算架构和编程 | Assignment 1: Performance Analysis on a Quad-Core CPU

from pixivvAssignment 1: Performance Analysis on a Quad-Core CPU Environment SetupCPU信息 Architecture: x86_64CPU op-mode(s): 32-bit, 64-bitAddress sizes: 46 bits physical, 57 bits virtualByte Order: Little Endian C…

Contest3923 - 计科23级算法设计与分析上机作业-03

A.质数 题面思路 考虑到输入数据量较大,选择线性欧拉筛预处理 示例代码 #include<bits/stdc++.h>using namespace std;#define ll long long //#define int ll #define pii pair<int, int> #define all(x) x.begin(),x.end() #define fer(i, m, n) for(int i = m;…

leetcode 4. 两个有序数组的中位数(第k大的数)

假设有前 k 小的数,分配到两个数组中综上, 前k-1数的边界偏离(k-1)/2 时,由于大于(k-1)数边界的挤压会伴随小于k的数的边界的外延, 其在(k-1)/2会呈现一方比另一方大的情况,可以直接判定小的一方在小于k的数的边界内 而当k-1数正好在边界内,则同样可以判定小的数在小于k的…