当前位置: 首页 > news >正文

笔记 基础算法

CF547B - Description

给你个长度为 \(n\) 的序列 \(a\),对于每个 \(1\le k\le n\),有 \(n-k+1\) 个中所有长度为 \(k\) 的子串,你需要求出这 \(n-k+1\) 个子串的区间最小值的最大值,即下面式子的值:

\[\max_{l=1}^{n-k+1} \{\ \min_{i=l}^{l+k-1} a_i\ \} \]

\(1\le n\le 2\times 10^5\)

CF547B - Solution

首先考虑转化这个问题。假设有 \(l\le i\le r\),其中 \(a_i\) 是区间 \(\left [ l,r\right ]\) 的最小值,那么 \(a_i\) 一定具有这样一个性质:

  • 对于所有 \(j<i\),若 \(a_i<a_j\),则有 \(j<l\)
  • 对于所有 \(j>i\),若 \(a_i<a_j\),则有 \(j>r\)

针对这个性质,我们可以在 \(k\) 为定值时将问题转化为一个判定性问题。假设我们目前求出了 \(lft_i\)\(rgt_i\) 来分别表示在 \(i\) 前面第一个小于 \(a_i\) 的数的位置和在 \(i\) 后面的第一个小于 \(a_i\) 的数的位置,那么 \(a_i\) 可以是 $\left [ lft_i+1,rgt_i-1\right ] $ 中任意一个子区间的区间最小值,因此 \(a_i\) 会对所有 \(k\le rgt_i-lft_i-1\) 的答案做出贡献(下面将记作 \(b_i\))。这里的 \(rgt\)\(lft\) 我们可以使用单调栈 \(O(n)\) 求出,而我们注意到对所有元素从大到小排序,第一个扫到的 \(b_i\) 一定就是最大的,后面不会再被更新,于是我们将 \((b_i,a_i)\) 存为二元组后对 \(a_i\) 从大到小排序。排完序扫一遍,如果每次从上一个更新后的 \(b_{lst}+1\) 开始扫,扫到 \(b_i\),中间所有答案都赋值为 \(a_i\)。因为所有位置都只会被扫到一次,所以时间复杂度为 \(O(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,a[200005],lft[200005],rgt[200005],ava[200005],ans[200005],lst;
stack<int>q,qq;
struct node{int val,av;
}e[200005];
inline bool cmp(node x,node y){if(x.val==y.val){return x.av>y.av;}return x.val>y.val;
}
signed main(){cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){while(!q.empty()&&a[q.top()]>a[i]){rgt[q.top()]=i;q.pop();}q.push(i);}for(int i=n;i>=1;i--){while(!qq.empty()&&a[qq.top()]>a[i]){lft[qq.top()]=i;qq.pop();}qq.push(i);}for(int i=1;i<=n;i++){if(rgt[i]==0){rgt[i]=n+1;}ava[i]=rgt[i]-lft[i]-1;e[i].val=a[i];e[i].av=ava[i];}sort(e+1,e+1+n,cmp);for(int i=1;i<=n;i++){int val=e[i].val;for(int j=e[lst].av+1;j<=e[i].av;j++){lst=i;ans[j]=val;}}for(int i=1;i<=n;i++){cout<<ans[i]<<" ";}return 0;
}

ABC352D - Description

给你一个 \(n\) 的排列 \(a\),让你选出一个长度为 \(k\)\(a\) 的子序列 \(b=\left [ a_{p_1},a_{p_2},\cdots ,a_{p_k} \right ]\),使得 \(\min b_i +k-1=\max b_i\) 的同时控制 \(p_k-p_1\) 最小,求出这个最小值。

\(1\le n,k\le 2\times 10^5\)

ABC352D - Solution

注意到 \(a\) 数组本身就是排列,而要求选出排列就等于直接截取 \(a\) 的子串,这省去了中间的很多过程。先求出 \(c_{a_i}=i\),可以发现 \(c\)\(a\) 的一个置换,因此显然不影响答案。要让 \(b\) 数组构成一个排列 \(\{ a,a+1,\cdots ,a+k-1\}\),就是在 \(c\) 中选出一个长度为 \(k\) 的子串。而 \(p_k\) 就是这个子串中的最大 \(c_i\)\(p_1\) 就是最小 \(c_i\)。现在,答案就变成了下面式子的值:

\[\min_{i=1}^{n-k+1} (\max_{j=i}^{i+k-1} c_i - \min_{j=i}^{i+k-1} c_i) \]

单调队列扫描可以做到 \(O(n)\) 预处理。

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,k,a[200005],c[200005],minn[200005],maxx[200005],ans=INT_MAX;
deque<int>q;
signed main(){cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];c[a[i]]=i;}
//	for(int i=1;i<=n;i++){
//		cout<<c[i]<<" ";
//	}
//	cout<<endl;for(int i=1;i<=n;i++){while(!q.empty()&&i-q.front()>=k){q.pop_front();}while(!q.empty()&&c[q.back()]>c[i]){q.pop_back();}q.push_back(i);minn[i]=c[q.front()];}while(!q.empty()){q.pop_back();}for(int i=1;i<=n;i++){while(!q.empty()&&i-q.front()>=k){q.pop_front();}while(!q.empty()&&c[q.back()]<c[i]){q.pop_back();}q.push_back(i);maxx[i]=c[q.front()];}for(int i=1;i<=n-k+1;i++){int x=i+k-1;ans=min(ans,maxx[x]-minn[x]);}cout<<ans<<endl;return 0;
}

CF1407D - Description

\(n\) 栋楼,每栋楼有高度 \(h_i\),对于第 \(i\) 栋楼和第 \(j\) 栋楼,如果 \((i,j)\) 满足以下三个条件中的任意一个,我们认为可以从第 \(i\) 栋楼跳到第 \(j\) 栋楼:

  • \(i+1=j\)
  • \(\max(h_{i+1},h_{i+2},\cdots ,h_{j-1})<\min(h_i,h_j)\)
  • \(\min(h_{i+1},h_{i+2},\cdots ,h_{j-1})>\max (h_i,h_j)\)

问你从第 \(1\) 栋楼开始,最少跳几次能跳到第 \(n\) 栋楼?

\(2\le n\le 3\times 10^5\)

CF1407D - Solution

我们设计一个 \(f_i\) 表示从 \(1\) 走到 \(i\) 的最少步数。

对于限制 \(1\),容易得到 \(f_i=f_{i-1}+1\)

限制 \(2\) 就是说对于所有 \(i<k<j\)\(i\) 都是 \(k\) 左侧第一栋比 \(h_k\) 高的楼,\(j\) 都是 \(k\) 右侧第一栋比 \(h_k\) 高的楼。这种左边/右边第一个大于/小于的题很容易想到单调栈求出 \(lft\)\(rgt\)

我们从 \(i\) 往前找,遍历每一个 \(h_j<h_i\) 的点,如果 \(j\) 满足 \(\max\{ h_{j+1},\cdots ,h_{i-1} \}<h_j\),就可以从 \(j\) 跳到 \(i\)。我们发现这个条件就等于在讲对于 \(i\) 更新单调栈时会弹出 \(j\),于是维护单调栈的时候顺手计算就行。一个细节是如果 \(h_j=h_i\),是无法从 \(j\) 跳到 \(i\) 的,注意特判。

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,h[300005];
stack<int>q,qq;
long long dp[300005];
signed main(){cin>>n;for(int i=1;i<=n;i++){cin>>h[i];dp[i]=INT_MAX;}dp[1]=0;q.push(1);qq.push(1);for(int i=2;i<=n;i++){int lst=INT_MAX;while(!q.empty()&&h[q.top()]>=h[i]){dp[i]=min(dp[i],dp[q.top()]+1);lst=h[q.top()];q.pop();}if(!q.empty()&&lst!=h[i]){dp[i]=min(dp[i],dp[q.top()]+1);}q.push(i);lst=INT_MAX;while(!qq.empty()&&h[qq.top()]<=h[i]){dp[i]=min(dp[i],dp[qq.top()]+1);lst=h[qq.top()];qq.pop();}if(!qq.empty()&&lst!=h[i]){dp[i]=min(dp[i],dp[qq.top()]+1);}qq.push(i);}cout<<dp[n]<<endl;return 0;
}

P1080 - Description

\(n+1\) 个具备 \(a\)\(b\) 两个数值的人(第 \(0\) 个人不参与计算,但是永远在所有人前面),对所有 \(1\le i\le n\),定义 \(f(i)\)

\[f(i)=\left \lfloor \frac{\prod_{j=0}^{i-1} a_j }{b_i}\right \rfloor \]

现在让你重新排列队伍,使最大的 \(f(i)\) 最小。(\(1\le i\le n\)

\(1\le n\le 1000,1\le a_i,b_i\le 10^4\)

P1080 - Solution

像这种存在前后关系,并且操作满足交换律的题目我们应当警惕。这告诉我们这种贪心可以使用临项交换解决。具体地,我们考虑相邻的两项 \(i\)\(i+1\),有:

\[f(i)=\left \lfloor \frac{a_0a_1\cdots a_{i-1}}{b_i}\right \rfloor \]

\[f(i+1)=\left \lfloor \frac{a_0a_1\cdots a_{i}}{b_{i+1}}\right \rfloor \]

我们考虑交换 \(i\)\(i+1\) 对答案的影响。

\[f(i)'=\left \lfloor \frac{a_0a_1\cdots a_{i-1}a_{i+1}}{b_i}\right \rfloor \]

\[f(i+1)'=\left \lfloor \frac{a_0a_1\cdots a_{i-1}}{b_{i+1}}\right \rfloor \]

如果 \(\arg \max f(i)\) 不是 \(i\)\(i+1\),临项交换前后是对答案没有影响的;如果是 \(i\)\(i+1\),我们发现交换操作就等同于给 \(f(i)\) 乘上 \(a_{i+1}\),给 \(f(i+1)\) 除以 \(a_i\)。如果 \(i\) 交换前更优,那么:

\[\left \lfloor \frac{a_0a_1\cdots a_{i}}{b_{i+1}}\right \rfloor<\left \lfloor \frac{a_0a_1\cdots a_{i-1}}{b_i}\right \rfloor<\left \lfloor \frac{a_0a_1\cdots a_{i-1}a_{i+1}}{b_i}\right \rfloor \]

也就是说:

\[\frac{a_i}{b_{i+1}}<\frac{a_{i+1}}{b_i} \]

化简一下,我们得到了 \(a_ib_i<a_{i+1}b_{i+1}\) 的结论。也就是说,如果 \(a_ib_i<a_{i+1}b_{i+1}\),那么交换后一定是不优的。于是我们按照 \(a_ib_i\) 从小到大排序,扫描一遍得出答案即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,ans;
struct node{int a,b;
}e[1005];
inline bool cmp(node x,node y){return x.a*x.b<y.a*y.b;
}
signed main(){cin>>n;for(int i=0;i<=n;i++){cin>>e[i].a>>e[i].b;}sort(e+1,e+1+n,cmp);int fac=e[0].a;for(int i=1;i<=n;i++){ans=max(ans,fac/e[i].b);fac*=e[i].a;}cout<<ans<<endl;return 0;
}
http://www.hskmm.com/?act=detail&tid=26798

相关文章:

  • P10068 [CCO 2023] Line Town
  • AI元人文:共识锚定与智慧剪枝——构建人机共生认知经济体的完善理论体系与实践路径
  • 出题系统
  • io控制方式
  • Java课后作业
  • 我 是 人 机
  • 28定律及其扩展衍生
  • 3516cv610在sample_aiisp上多创一路编码流,方法 - 详解
  • 2025氧化石墨烯、羧基化石墨烯、巯基化石墨烯、羟基化石墨烯、氨基化石墨烯、氮掺杂氧化石墨烯、氮掺杂石墨烯最新推荐、全面剖析优质厂商实力与选购要点
  • 2025-10-8模拟测验
  • QBXT2025S刷题 Day7
  • 【Python】调用C++
  • 方法作业
  • [100ask_imx6ullpro] buildroot构建emmc镜像并烧录
  • 2025 汽车改装公司最新推荐榜:一站式服务生态企业盘点,含奔驰宝马新能源改装及新锐品牌权威测评重庆宝马汽车改装/重庆新能源汽车改装/重庆汽车改装贴膜/重庆汽车改装轮毂刹车公司推荐
  • 2025 布袋包装厂家最新推荐榜:自贸区实力厂商领衔,含手提袋、帆布袋等全品类,年销 500 万级生产商精选无纺布袋/布袋生产/云南布袋包装/茶叶布袋厂家推荐
  • 2025 年成型机厂家最新推荐排行榜:冷弯 / 光伏支架 / 门业 / 建材等领域设备企业精度与耐用性实测点评魔方方管/门框角码/导槽/底樑/光伏支架/C型钢成型机厂家推荐
  • 2025 年平板机厂家最新推荐榜单:聚焦技术实力与市场口碑,5 大优质品牌实测点评
  • 语音识别与合成的融合技术解析
  • 2025 年阳光导入源头厂家最新推荐榜:领军企业技术实力、案例与直销模式深度解析及选择指南工厂/学校/医院/地下车库/隧道阳光导入系统厂家推荐
  • 从Node.js到React/Vue3:流式输出实用的技术的全栈实现指南
  • 用低成本FPGA实现FSMC接口的多串口(UART)控制器
  • 2025 火烧板源头厂家最新推荐榜单:自有矿山保障品质,高硬度耐磨产品全覆盖,五莲花 / 芝麻白 / 防滑芝麻黑采购优选指南
  • 2025 年太阳能路灯厂商最新推荐榜:聚焦优质企业,从技术实力到合作案例全方位解析太阳能道路灯/景观灯/警示灯/庭院灯/草坪灯/杀虫灯厂家推荐
  • 2025 年最新软件开发机构推荐排行榜:涵盖 CRM / 物联网 / 运维管理等系统定制的权威甄选指南成都软件开发/软件定制开发/crm系统定制软件开发机构推荐
  • Luogu P11660 我终将成为你的倒影 题解 [ 紫 ] [ 分块 ] [ 分类讨论 }
  • 2025 年最新推荐!小程序开发机构排行榜:覆盖定制开发 / 电商 / 预订 / 配送多场景优质服务商成都小程序开发/小程序定制开发/电商小程序开发/预订服务小程序开发公司推荐
  • CF280D k-Maximum Subsequence Sum 题解(线段树+反悔贪心维护k段最大子段和)
  • 2025 西安新房住宅最新推荐榜权威发布:多维度测评 + 选房指南,助你精准置业品质/高端/优质/品牌/刚需新房推荐
  • C# async await 测试一