2024年1月24日欢乐赛~Happy
A stong9070奇遇记之合并序列
求两个递增序列合并成一个新的的递增序列后,原来的每个元素处在新序列的第几个位置。
数据范围:\(1≤N,M≤10^5\)
分析
两个有序序列合并为一个新的有序序列,可以想到归并排序的合并操作
对于之后的查询可以使用二分。
复杂度:合并 \(O(n+m)\),查询 \(O((n+m)log_2n)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,a[N<<1],b[N<<1];
int main(){
cin>>n>>m;
for(int i=1; i<=n+m; i++) cin>>a[i];
int i=1, j=n+1, p=0;
while(i<=n && j<=n+m){
if(a[i] <= a[j]) b[++p] = a[i++];
else b[++p] = a[j++];
}
while(i<=n) b[++p] = a[i++];
while(j<=n+m)b[++p] = a[j++];
for(int i=1; i<=n+m; i++){ // >=
int x = lower_bound(b+1, b+1+p, a[i]) - b;
cout<<x<<" ";
if(i==n) cout<<endl;
}
return 0;
}
B 最后一块石头的重量
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设石头的重量分别为x 和 y,且 x⩽y。那么粉碎的可能结果如下:
- 如果 x==y,那么两块石头都会被完全粉碎;
- 如果 x!=y,那么重量为 x的石头将会完全粉碎,而重量为 y 的石头新重量为 y−x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
数据范围:1⩽n⩽30,1⩽ 石头重量 ⩽1000.
分析
按照要求模拟,每次选择最大的两个石头,使用大顶堆来维护最大值,每次将维护的数据更新后加入堆中,直到堆内元素数量为1。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,x;
int main(){
priority_queue<int> q; // 大顶堆
cin>>n;
for(int i=1; i<=n; i++) {
cin>>x; q.push(x);
}
while(q.size() > 1){
int y = q.top(); q.pop();
int x = q.top(); q.pop();
if(x!=y) q.push(y-x);
}
if(q.size()==0) q.push(0);
cout<<q.top();
return 0;
}
C 【基础】探测池塘
寻找出现在至少两个的相邻读数中的最大的深度(相邻指的是一格的周围的八个格子)。
数据范围:数据范围:1⩽n⩽50.
分析
枚举每一个点(i,j) 看其是不是最大深度。
复杂度:\(O(8nm)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=55;
int d[][2] = {-1,-1,-1,0,-1,1,0,-1,0,0,0,1,1,-1,1,0,1,1};
int n,m,g[N][N],ans;
int dfs(int x,int y) {
int t=0;
for(int i=0; i<9; i++) {
int tx=x+d[i][0],ty=y+d[i][1];
if(tx<1||tx>n||ty<1||ty>m|| g[tx][ty]==0) continue;
if(g[x][y] == g[tx][ty]) t++;
}
return (t>=2) ? g[x][y] : 0;
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>g[i][j];
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
ans = max(ans, dfs(i,j));
cout<<ans;
return 0;
}
D 新二叉树
输入一棵二叉树,输出其先序遍历序列。
数据范围:1≤n≤26
分析
先建树,发现数据较小,于是可以利用数据本身作为下标索引
abc ---> tr[a] = {b,c}
左子树:tr[a].l
左子树:tr[a].r
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=300;
int n, st[N];
struct Node{
char l,r;
}tr[N];
void dfs(char u){
if(u=='*') return;
cout<<u;
dfs(tr[u].l);
dfs(tr[u].r);
}
int main() {
cin>>n; char a,b,c;
for(int i=1; i<=n; i++) {
cin>>a>>b>>c;
st[b] = st[c] = 1;
tr[a] = {b,c};
}
for(int i='a'; i<='z'; i++)
if(!st[i]){ a = i; break; }
dfs(a);
return 0;
}
E 买雪糕
用 V 元来给 N 个人买雪糕,每个人都有一个对雪糕的要求 wi,雪糕的价格不能低于这个要求。雪糕店里有 M 种雪糕,每种雪糕的价格是 ai,最多能满足几个人的要求?
数据范围:1⩽V⩽10^9,3⩽N,M⩽100000, 1⩽wi,ai⩽10000
分析
思考一个策略,使得尽量多的人被满足
要求越低的人越容易被满足,先满足要求低的人
如果当前要求低的人都无法满足,那么要求高的人也一定无法满足。
对元素wi, ai分别升序排序后,利用双指针去匹配合适的物品,如果匹配不到那就无法满足。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,v,w[N],a[N];
int main() {
cin>>n>>m>>v;
for(int i=1; i<=n; i++) cin>>w[i];
for(int i=1; i<=m; i++) cin>>a[i];
sort(w+1, w+1+n);
sort(a+1, a+1+m);
int ans=0;
for(int i=1, j=1; i<=n; i++) {
while(j<=m && w[i] > a[j]) j++;
if(j<=m && v >= a[j]) v-=a[j++], ans ++;
}
cout<<ans;
return 0;
}
F stong9070奇遇记之叫号
有 N 个人去排队叫号,他们编号分别为 1 到 N,每个人手上都有一张其他人的号码 Ai。初始时,所有人没有被叫过。如果第 i 个人没有被叫过,他就叫自己手上的号码 Ai。
问整个叫号结束后,有多少人没有叫过,按升序输出结果。
数据范围:2≤N≤2×10^5, 1≤Ai≤N, Ai!=i.
分析
如过没有被叫过,才会叫手上的号码,考虑使用标记思想,如果某人被叫,就标记。
map<int,int> mp; 申请出战
mp[a[i]]=1; 表示 a[i] 被叫过
mp.count(i)==0; 表示 i 没有被叫过
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,a[N];
map<int,int> mp;
int main() {
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i];
for(int i=1; i<=n; i++)
if(mp.count(i) == 0) mp[a[i]] = 1;
int ans=0;
for(int i=1; i<=n; i++)
if(mp.count(i)==0) ans++;
cout<<ans<<endl;
for(int i=1; i<=n; i++)
if(mp.count(i)==0) cout<<i<<" ";
return 0;
}