1542 字
4 分钟
日常算法随记part.2
2026-04-07
统计加载中...

忙里偷闲来更新一下我缓慢的学习进度(),这篇主要还是备战下周蓝桥杯做的题目,还有上星期刚打完的校赛的补题。

周末蓝桥杯rp++

K - 给点…资金#

题目链接:https://ac.nowcoder.com/acm/contest/130737/K
一道二分题,当时赛场上想到了O(n2)O(n^2)的暴力模拟方法,没想到可以二分优化。后面问了其他大佬之后一语点醒梦中人了。
大致题意:有nn个价值为aia_i卡牌,商店在第ii天有相应的倍率bib_i。在第ii天出售第jj张牌可以获得 aj×bia_j \times b_i元。每张牌只能出售一次,但可以自己决定哪一天出售。现希望可以尽快卖到xx元,并输出最快达到kk元的是第几天。

思路:根据题意我们可知,想要收益最大化肯定是要尽可能让在高倍率的天出售高价值的卡牌。那么我们可以先对AA数组从大到小排列,然后让在对应的天数区间里面按大倍率配大价值的规律计算。这里查找可能的天数时采用了二分答案,优化我们搜索的过程,否则会超时。最终输出满足条件的最小天数。
我们这里用了一个CC数组来存入我们选取的第midmid天内的BB数组存有的值。然后我们按从大到小排列,与AA数组一一相乘后计入总和。如果达到xx就继续向左取半搜索,看看有没有更小的值。没有达到xx就向右取半搜索,找到第一个满足条件的midmid。这里因为二分查找的时间复杂度为O(logN)O(log N),所以这种解法的最终复杂度其实只有O(NlogN)O(NlogN),可以轻松通过这道题目。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl '\n'
#define mod 998244353
void solved(){
ll n, x;
cin >> n >> x;
vector<ll> A(n);
vector<ll> B(n);
for (int i = 0; i < n;i++){
cin >> A[i];
}
for (int i = 0; i < n;i++){
cin >> B[i];
}
sort(A.begin(), A.end(), greater<ll>());
ll l = 1, r = n, ans = -1;
while(l <= r){
ll mid = (l + r) / 2;
vector<ll> C(B.begin(), B.begin() + mid);
sort(C.begin(), C.end(),greater<ll>());
ll sum = 0;
for (ll i = 0; i < mid;i++){
sum += A[i] * C[i];
if(sum >= x){
break;
}
}
if(sum >= x){
r = mid - 1;
ans = mid;
}
else{
l = mid + 1;
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solved();
return 0;
}

P10426 宝石组合#

需要化简题目给的条件。然后再找到最小的可能的因数。只不过后者代码上也不是那么好实现。
题目链接:https://www.luogu.com.cn/problem/P10426
大致题意:给定nn个属性值为HiH_i的宝石。我们会随机从这nn枚宝石里面选出三枚来进行组合,组合成的精美程度SS计算方式为 S=HaHbHcLCM(Ha,Hb,Hc)LCM(Ha,Hb)LCM(Ha,Hc)LCM(Hb,Hc)S = H_a H_b H_c \cdot \frac{LCM(H_a,H_b,H_c)}{LCM(H_a,H_b) \cdot LCM(H_a,H_c) \cdot LCM(H_b,H_c)}LCMLCM表示的是最小公倍数。现在要求输出SS最高的组合,如果有多个方案只需输出字典序最小的三个。

思路:这题我们需要先对原式进行化简,题解里的大佬都是各显神通的,我就不掺和了。()我这里直接使用公式LCM(x,y)=xygcd(x,y)LCM(x,y) = \frac{xy}{gcd(x,y)}, LCM(x,y,z)=xyzgcd(x,y,z)gcd(x,y)gcd(x,z)gcd(y,z)LCM(x,y,z) = \frac{xyz \cdot gcd(x,y,z)}{gcd(x,y) \cdot gcd(x,z) \cdot gcd(y,z)}。代入原式可以解得S=gcd(a,b,c)S = gcd(a,b,c)。所以我们的问题就转化为要使得组合成的三个数的最大公因数最大了。
解决方法还是有点需要一点技巧的。我们需要先找出所有宝石里面的有的因数。然后用cnt[factor]cnt[factor]记录其出现的数量。这里优化复杂度的方法是找到一个因数之后直接用原数除于它就可以找到另外一个对应较大因数。(如果另一个因数是本身就不计入)计入对应的cntcnt中。接着我们倒序查找cnt[factor]cnt[factor]出现次数大于等于33次的因数factorfactor的最大值并用xx记录。然后我们正序从小到大判断xxHiH_i的因数的数并输出前三个即可。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl '\n'
#define mod 1000000007LL
typedef pair<int, int> pii;
vector<ll> H;
int cnt[200010];
void solved(){
int n;
int x = 0;
cin >> n;
H.resize(n + 1);
for (int i = 1; i <= n;i++){
cin >> H[i];
for (int j = 1; j * j <= H[i];j++){
if(H[i] % j == 0){
if(j * j != H[i]) cnt[H[i] / j]++;
cnt[j]++;
}
}
}
sort(H.begin() + 1, H.begin() + 1 + n);
for (int i = 1e5; i >= 1;i--){
if(cnt[i] >= 3){
x = i;
break;
}
}
int cnt1 = 0;
for (int i = 1; i <= n;i++){
if(cnt1 == 3){
break;
}
if(H[i] % x == 0){
cnt1++;
cout << H[i] << " ";
}
}
}
int main(){
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solved();
return 0;
}

P10429 拔河#

双指针优化的题目,个人觉得比较吃技巧,想要去优化这个查找和计算答案的过程实在不好想。
题目链接:https://www.luogu.com.cn/problem/P10429
大致题意:给定nn个力量值为aia_i的同学,我们要从中挑选出两个队伍。满足以下条件:{al1,al1+1,ar11,ar1}\{ a_{l_1},a_{l_1 + 1},\dots a_{r_1 - 1},a_{r_1} \}{al2,al2+1,,ar21,ar2}\{ a_{l_2},a_{l_2 + 1},\dots , a_{r_2 - 1}, a_{r_2} \},其中l1r1l2r2l_1 \leq r_1 \leq l_2 \leq r_2。两个队伍的人数不必相同,现在要使两个队伍内的力量值总和之差最小,输出能达到这个最小值。

思路:这道题给的数据nn1e31e3,也就是说我们最多只能使用到O(N3)O(N^3)。(但实际上O(N3)O(N^3)也是过不了的)最保险的还是是O(N2logN)O(N^2logN)。但目前比较直观直接暴力枚举加前缀和预处理优化最终的复杂度也还是会有O(N4)O(N^4),我们必须另寻他法继续优化。
这里我参考大佬的题解用了一种类似预处理答案的做法。核心思路是在每一次组成队伍的时候就查找可能的最小值,并在过程中不断更新。循环两次N2N^2,加上查找最近值logNlogN,恰好能满足复杂度的要求。
我们用前缀和预处理求和,然后用一个有序集合存储第一个队伍可能的力量值总和。每一次循环,对于未在第一个队伍的同学就计算可能组成的第二个队伍的力量的总和的值。然后再在集合中二分查找找到一个最相近的值减去,更新ansans。最后结束时,我们再更新当第一支队伍取到第i+1i + 1个同学时,可能组成的队伍的力量值总和。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl '\n'
#define mod 998244353
void solved(){
int n;
cin >> n;
vector<ll> A(n + 1);
for (int i = 1; i <= n;i++){
cin >> A[i];
}
if(n == 1){
cout << 0 << '\n';
return;
}
vector<ll> B(n + 1);
for(int i = 1; i <= n;i++){
B[i] = B[i - 1] + A[i];
}
multiset<ll> Lsum;
Lsum.insert(A[1]);
ll ans = LLONG_MAX;
for (int j = 2; j <= n;j++){
for (int r = j; r <= n;r++){
ll sumr = B[r] - B[j - 1]; //j每次循环第二支队伍有n - r种可能的力量总值。
auto it = Lsum.lower_bound(sumr);
if(it != Lsum.end()){ //第一个大于等于sumr的判断一次
ans = min(ans, abs(sumr - *it));
}
if(it != Lsum.begin()){ //第一个小于sumr的判断一次
--it;
ans = min(ans, abs(sumr - *it));
}
}
for (int l = 1; l <= j;l++){ //取到下一个新同学后,更新添加suml里的元素
ll suml = B[j] - B[l - 1];
Lsum.insert(suml);
}
}
cout << ans;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solved();
return 0;
}

P12349 翻转硬币#

比较难的一道dpdp,对于我这种蒟蒻备赛来说其实没有什么补题的必要了。()不过还是贴一下我学习时ACAC的代码吧,详细解析可以参考一下洛谷该题题解的第一个,再加AIAI硬啃一下。(我就是这样)
题目链接:https://www.luogu.com.cn/problem/P12349
参考题解:https://www.luogu.com.cn/problem/solution/P12349
大致题意:给定nnmm列的硬币,每一个硬币的价值取决于上下左右相邻的硬币中与其正反相同的硬币数之和的平方。我们可以进行任意次操作让任意一行的硬币翻转。求硬币的价值之和最大可能是多少。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
int n, m;
int dp[1010][2]; //1.第i行。2.是否和原来相反(0为相同,1为不同)
int a[1010][1010]; //dp[i][{0,1}]为前i - 1行中第i行与第i - 1行的贡献
char c[1010][1010];
//calc,p1为第x行和第x-1行的状态{0,1},p2为第x行和第x+1行的状态{0,1}
int calc(int x, int p1,int p2){
int sum = 0;
for (int j = 1; j <= m; j++){
int cnt = 0;
if(x > 1){
cnt += ((a[x][j] ^ p1) == a[x - 1][j]);
}
if(x < n){
cnt += ((a[x][j] ^ p2) == a[x + 1][j]);
}
if(j > 1){
cnt += (a[x][j] == a[x][j - 1]);
}
if(j < m){
cnt += (a[x][j] == a[x][j + 1]);
}
sum += cnt * cnt;
}
return sum;
}
void solved(){
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++){
a[i][j] = c[i][j] - '0';
}
}
dp[2][0] = calc(1, 0, 0);
dp[2][1] = calc(1, 1, 1);
for (int i = 3; i <= n + 1;i++){
dp[i][0] = max(dp[i - 1][0] + calc(i - 1, 0, 0), dp[i - 1][1] + calc(i - 1, 1, 0));
dp[i][1] = max(dp[i - 1][0] + calc(i - 1, 0, 1), dp[i - 1][1] + calc(i - 1, 1, 1));
}
cout << max(dp[n + 1][0], dp[n + 1][1]);
}
int main(){
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solved();
return 0;
}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

日常算法随记part.2
https://mkrari.cn/posts/algorithmrecord_2/
作者
Mkrari
发布于
2026-04-07
许可协议
CC BY-NC-SA 4.0