济南 CSP-J 刷题营 Day4 数据结构
Solution
T1 出现次数
原题链接
简要思路
利用类似前缀和的 “后缀和” 来记录下每个数后面有几个未重复出现的数,定义一个
完整代码
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN=1e5+5; int n,q; int f[MAXN];//判断是否是第一次出现 int hzh[MAXN];//后缀和 int a[MAXN]; signed main(){ cin>>n>>q; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=n;i>=1;i--){//倒序遍历 if(f[a[i]]==0){//如果是第一次出现 hzh[i]=hzh[i+1]+1; f[a[i]]=1;//标记已经出现 } else{ hzh[i]=hzh[i+1]; } } while(q--){ int x; cin>>x; cout<<hzh[x]<<endl;//直接输出就行 } return 0; }
T2 组模拟赛
原题链接
简要思路
设当前已经举办了 ans++
。
完整代码
#include<bits/stdc++.h> #define int long long using namespace std; int n,m; int a[100005]; int f[100005];//记录每种难度的比赛出现几次 int num[100005];//num[i] 代表第 i 场比赛的举办时间 signed main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; if(n==m){//特判 cout<<n<<endl<<n<<endl; return 0; } int ans=0; for(int i=1;i<=n;i++){ f[a[i]]++;//难度为 a[i] 的题的数量 +1 if(i<m)continue;//如果数量过少,不可能组成一场模拟赛 else{ bool yes=1; for(int j=1;j<=m;j++) if(f[j]<=ans){//如果不够数量,不能够组成 yes=0; break; } if(yes==1)num[++ans]=i;//记录 } } cout<<ans<<endl; for(int i=1;i<=ans;i++) cout<<num[i]<<' '; return 0; }
T3 完成作业
原题链接
简要思路
第一种做法
对数组按照时间进行倒序排序,如果时间相同就取分数最大的,如果不选就把时间往前推。整个过程用优先队列(堆)来实现。
第二种做法
对数组按照时间进行正序排序,每个时间点先不用管后面的,先选当前的最大值,如果后面有比之前更优的选择就更改替换之前的操作,同样要哪堆来实现这个反悔贪心。
完整代码
#include<bits/stdc++.h> using namespace std; const int MAXN=1e5+5; pair<int,int> a[MAXN]; priority_queue<int,vector<int>,greater<int>> q;//优先队列(堆) int n int now,last,ans; signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i].first>>a[i],second; sort(a+1,a+n+1);//按照时间进行正序排序 for(int i=1;i<=n;i++){//反悔贪心 now+=a[i].first-last; last=a[i].first; if(now>0){ now--; ans+=a[i].second; q.push(a[i].second); } else if(q.top()<a[i].second){ ans-=q.top(); ans+=a[i].second; q.pop(); q,push(a[i].second); } } cout<<ans<<endl; return 0; }
T4 涂刷油漆
原题链接
简要思路
处理出每个操作来看能刷到什么高度(也就是
看一个柱子的最大高度是经过该柱子的所有区间的最大值。
用贪心,在保证
如果未到达最大高度,我们就要进行多一次操作,但是要保证对后边的柱子影响越多。
在高度最大的条件下位置最右的区间。
记录延伸到了哪,高度是多少。
完整代码
代码挂了,在调,回来补。QAQ__
施工中ing~~~
讲课笔记
前缀和和差分
提前记录下,以防 TLE,这样可以实现 O(1) 的查询输出。
-
一维前缀和与差分
-
多维前缀和与差分
拆分开进行计算。 -
树上前缀和与差分
-
高维前缀和(欧氏空间上)
-
高维前缀和(偏序集的直积空间上)
Hash 表
把字符串通过某一种方法替换为一个数字来进行比较两个字符串是否相等。
-
字符串 Hash
单模哈希、双模哈希、自然溢出。 -
探查法/拉链法
链表、栈、队列
比较简单,范 sir 讲过。
这里要注意一个坑:双端队列的 deque
空间特别大,能不用就别用,容易挂分。
简单提一下这三种数据结构的用处(可是都不会)。
- 链表
-
快速插入、删除
-
区间覆盖问题
- 栈
-
表达式树
-
单调栈
-
笛卡尔树
-
离线 RMQ
- 队列
-
队列和双端队列
-
单调队列
-
优先队列(一般为二叉堆,俗称 堆)
-
优化 DP
倍增、并查集
zhx 巨佬讲过这两种算法,简单带一下。
倍增
给定
-
核心思想:
代表从 开始的 个数的最大值。 -
小技巧:
求 个数的什么东西,区间太长,一般会在中间“砍一刀”,最后只保留一个数。 -
算法(处理
):
即:
f[i][0]=a[i]
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1])
- 做法(用
来算答案):
性质:一个数被算多次在求最大值中也不会影响结果。
具体做法:一个区间长为
#include<bits/stdc++.h> using namespace std; //n<=100000 //所以f的第二个维度只需要保证2^j>=n即可 int n,a[100005],f[100005][20]; int x[100005]; //x[i]代表长度为i的区间 用两个长度为2^x[i]的区间能够覆盖 signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; x[1]=0; for(int i=2;i<=n;i++) x[i]=x[i>>1]+1;//算j //x[111]=x[55]+1 for(int i=1;i<=n;i++) f[i][0]=a[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);//预处理 int T; cin>>T; while(T--){ int l,r; cin>>l>>r; int len=r-l+1; cout<<max(f[l][x[len]],f[r-(1<<x[len])+1][x[len]])<<endl;//算结果 } }
可以用来实现 LCA
并查集
有
添加
如果
int go(int p){//看一下点 p 沿着并查集箭头最后会走到哪里 if(to[p]==p)return p;//指向自己 else return go(to[p]);//递归调用 }
可以用来实现 Kruscal
如果两个点在并查集中可以不断走向同一个点,那么这两个点就属于同一个连通块。
题单
今日总结
-
有不会的问题抓紧时间问老师,争取当天弄懂。
-
赛时不要怕,直接暴力,可以分讨暴力骗分。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端