刷洛谷题单【数据结构2-1】
作者:互联网
P1090 合并果子
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 \(n-1\) 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 \(1\) ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 \(3\) 种果子,数目依次为 \(1\) , \(2\) , \(9\) 。可以先将 \(1\) 、 \(2\) 堆合并,新堆数目为 \(3\) ,耗费体力为 \(3\) 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 \(12\) ,耗费体力为 \(12\) 。所以多多总共耗费体力 \(=3+12=15\) 。可以证明 \(15\) 为最小的体力耗费值。
试做
代码
#include<iostream>
#include<queue>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long>>q;
long long total=0;
int main() {
int t; cin >> t;
long long x;
for(int i=0;i<t;i++) {
cin >> x;
q.push(x);
}
long long a, b;
for (int i = 0; i < t - 1; i++) {
a = q.top(); q.pop();
b = q.top(); q.pop();
total += a + b;
q.push(a+b);
}
cout << total;
}
批改
\(AC\)了
他山之石
P3865 【模板】ST 表
题目背景
这是一道 ST 表经典题——静态区间最大值
请注意最大数据时限只有 0.8s,数据强度不低,请务必保证你的每次查询复杂度为 \(O(1)\)。若使用更高时间复杂度算法不保证能通过。
如果您认为您的代码时间复杂度正确但是 TLE,可以尝试使用快速读入:
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
函数返回值为读入的第一个整数。
快速读入作用仅为加快读入,并非强制使用。
题目描述
给定一个长度为 \(N\) 的数列,和 $ M $ 次询问,求出每一次询问的区间内数字的最大值。
学
用我自己话说,ST 算法就是从两个端点分别相向出发,快速找到不超过另一个端点的大区间(至少超过一半)。合并两个区间答案就能得到查询区间答案(是因为max的特性:不能漏,但可以重)。
加速找区间的方法是倍增。ST算法本质是区间\(\rm dp\)。
看到了一个总结RMQ问题的文章:浅析RMQ问题
板子
引用自:https://www.luogu.com.cn/blog/attack/solution-p3865
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int Max[MAXN][21];
int Query(int l,int r)
{
int k=log2(r-l+1);
return max(Max[l][k],Max[r-(1<<k)+1][k]);//把拆出来的区间分别取最值
}
int main()
{
#ifdef WIN32
freopen("a.in","r",stdin);
#endif
int N=read(),M=read();
for(int i=1;i<=N;i++) Max[i][0]=read();
for(int j=1;j<=21;j++)
for(int i=1;i+(1<<j)-1<=N;i++)//注意这里要控制边界
Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);//如果看不懂边界的话建议好好看看图
for(int i=1;i<=M;i++)
{
int l=read(),r=read();
printf("%d\n",Query(l,r));
}
return 0;
}
P1878 舞蹈课
题目描述
有 \(n\) 个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从左到右站成一排。当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始跳舞。如果不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上(即:若队伍为 ABCD
,那么 BC
出列之后队伍变为 AD
)。舞蹈技术相差最小即是 \(a_i\) 的绝对值最小。
任务是模拟以上过程,确定跳舞的配对及顺序。
试做
思路
想法是用优先队列。
算法第一部分:扫描所有相邻的BG,记录id、数值,入队。
算法第二部分:
-
取队头,存入答案队列,标记\(vis\),然后按\(id\)前后搜索是否因为这一对出队,出现了新的BG。如果有,入队。
-
之后继续取队头,如果取出的元素已经vis,跳过。
算法第三部分:打印答案序列
代码
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 20010
struct Couple{
int id1,id2;
int a,b;
Couple(int id_1,int id_2,int aa,int bb){
id1=min(id_1,id_2);
id2=max(id_1,id_2);
a=min(aa,bb);
b=max(aa,bb);
}
bool operator<(const Couple &c)const {
return b-a>c.b-c.a;
}
void print()const{
cout<<id1<<' '<<id2<<endl;
}
};
priority_queue<Couple>q;//优先队列 用于处理数据
vector<Couple>qq;//答案序列
char str[N];
int num[N];bool vis[N];
int main(){
int t;cin>>t;
cin>>str+1;
for(int i=1;i<=t;i++)cin>>num[i];
for(int i=2;i<=t;i++){
if(str[i]!=str[i-1]){
q.push(Couple(i,i-1,num[i],num[i-1]));
}
}
while(!q.empty()){
Couple temp=q.top();
q.pop();
int t1=temp.id1,t2=temp.id2;
if(vis[t1]||vis[t2])continue;
vis[t1]=vis[t2]=true;
qq.push_back(temp);
while(vis[t1])t1--;
while(vis[t2])t2++;
if(str[t1]!=str[t2]){//新的Couple
q.push(Couple(t1,t2,num[t1],num[t2]));
}
}
cout<<qq.size()<<endl;
for(Couple c:qq){
c.print();
}
}
批改
分析
。。。不给下载样例我也不好分析,看题解罢
这篇题解跟我想得一模一样啊为什么我错了qwq
他山之石
https://www.luogu.com.cn/blog/qqq1112/solution-p1878
//代码里的register均可省去,是用于优化的
#include <bits/stdc++.h>
using namespace std;
pair < int, int > ans[1000001];//定义pair来存答案
int n, q[1000001], z;//z是答案总数
string s;
bool vis[1000001], f[1000001];//vis是用来判断是否选过这个人,f数组表示是男是女(男是true,女是false)
struct node
{
int x, y, z;//x为其中一个舞者的编号,y为另一个,z为舞技之差
friend bool operator < (node a, node b)
{
if(a.z == b.z)
{
return a.x > b.x;//priority_queue默认大根堆,要反过来写
}
else
{
return a.z > b.z;//同理
}
}
};//不用定义结构体数组
priority_queue < node, vector < node > > pru;//加不加vector都一样,加能更快一些
int main()
{
scanf("%d", &n);
cin >> s;
for(register int i = 0; i < n; ++i)
{
if(s[i] == 'B')//用f数组表示男女
{
f[i + 1] = true;//要加一(string是从0 ~ n - 1的)
}
else//不是男就是女
{
f[i + 1] = false;
}
}
for(register int i = 1; i <= n; ++i)
{
scanf("%d", &q[i]);
}
for(register int i = 1; i < n; ++i)
{
if(f[i] != f[i + 1])//不能男和男跳舞,女和女跳舞
{
pru.push((node){i, i + 1, abs(q[i] - q[i + 1])/*要加abs*/});//可以把它们强制转化到一个结构体里
}
}
while(!pru.empty())
{
int x = pru.top().x;//取出第一个人来
int y = pru.top().y;//取出第二个人来
pru.pop();//pop掉
if(!vis[x] && !vis[y])//不能有一个人跳过舞了
{
vis[x] = true;//改成跳过了
vis[y] = true;
ans[++z].first = x;//存答案
ans[z].second = y;//z只加一遍
//再找一对儿相邻的入队
//x向左边,y向右边(因为x小,y大)
while(x > 0 && vis[x])//跳过了或小于零都不行
{
--x;//往左边
}
while(y <= n && vis[y])//跳过了或大于n都不行
{
++y;//往右边
}
if(x > 0 && y <= n/*有点多余*/ && f[x] != f[y])//不能男和男或女和女啊
{
pru.push((node){x, y, abs(q[x] - q[y])});//强制转化成结构体再入队
}
}
}
printf("%d\n", z);//先输出总数
for(register int i = 1; i <= z; ++i)
{
printf("%d %d\n", ans[i].first, ans[i].second);//输出舞者
}
return 0;
}
P2251 质量检测
题目描述
为了检测生产流水线上总共 \(N\) 件产品的质量,我们首先给每一件产品打一个分数 \(A\) 表示其品质,然后统计前 \(M\) 件产品中质量最差的产品的分值 \(Q[m] = min\{A_1, A_2, ... A_m\}\),以及第 2 至第 \(M + 1\) 件的 $Q[m + 1], Q[m + 2] $... 最后统计第 \(N - M + 1\) 至第 \(N\) 件的 \(Q[n]\)。根据 \(Q\) 再做进一步评估。
请你尽快求出 \(Q\) 序列。
试做
\(ST\)的裸题
代码
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
int n,m,k;
int num[N];
int st[N][21];
int main(){
cin>>n>>m;
k=log2(m);
for(int i=1;i<=n;i++){
cin>>num[i];
st[i][0]=num[i];
}
for(int j=1;j<=k;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
for(int i=1;i<=n-m+1;i++){
cout<<min(st[i][k],st[i+m-(1<<k)][k])<<endl;
}
return 0;
}
批改
\(A\)了
P1801 黑匣子
题目描述
Black Box 是一种原始的数据库。它可以储存一个整数数组,还有一个特别的变量 \(i\)。最开始的时候 Black Box 是空的.而 \(i=0\)。这个 Black Box 要处理一串命令。
命令只有两种:
-
ADD(x)
:把 \(x\) 元素放进 Black Box; -
GET
:\(i\) 加 \(1\),然后输出 Black Box 中第 \(i\) 小的数。
记住:第 \(i\) 小的数,就是 Black Box 里的数的按从小到大的顺序排序后的第 \(i\) 个元素。
我们来演示一下一个有11个命令的命令串。(如下表所示)
序号 | 操作 | \(i\) | 数据库 | 输出 |
---|---|---|---|---|
1 | ADD(3) |
\(0\) | \(3\) | / |
2 | GET |
\(1\) | \(3\) | \(3\) |
3 | ADD(1) |
\(1\) | \(1,3\) | / |
4 | GET |
\(2\) | \(1,3\) | \(3\) |
5 | ADD(-4) |
\(2\) | \(-4,1,3\) | / |
6 | ADD(2) |
\(2\) | \(-4,1,2,3\) | / |
7 | ADD(8) |
\(2\) | \(-4,1,2,3,8\) | / |
8 | ADD(-1000) |
\(2\) | \(-1000,-4,1,2,3,8\) | / |
9 | GET |
\(3\) | \(-1000,-4,1,2,3,8\) | \(1\) |
10 | GET |
\(4\) | \(-1000,-4,1,2,3,8\) | \(2\) |
11 | ADD(2) |
\(4\) | \(-1000,-4,1,2,2,3,8\) | / |
现在要求找出对于给定的命令串的最好的处理方法。ADD
命令共有 \(m\) 个,GET
命令共有 \(n\) 个。现在用两个整数数组来表示命令串:
- \(a_1,a_2,\cdots,a_m\):一串将要被放进 Black Box 的元素。例如上面的例子中 \(a=[3,1,-4,2,8,-1000,2]\)。
- \(u_1,u_2,\cdots,u_n\):表示第 \(u_i\) 个元素被放进了 Black Box 里后就出现一个
GET
命令。例如上面的例子中 \(u=[1,2,6,6]\) 。输入数据不用判错。
不会啊,看看题解
他山之石
https://www.luogu.com.cn/blog/119261/solution-p1801,用大小根堆的方法,大佬称之为对顶。
下面是洛谷最高赞的代码,比较简短https://www.luogu.com.cn/blog/Sooke/solution-p1801
#include<cstdio>
#include<queue>
using namespace std;
int a[200005];
int main(){
priority_queue<int>A;
priority_queue<int,vector<int>,greater<int>>B;
int n,m,r=1,q;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
scanf("%d",&q);
for(int j=r;j<=q;j++){
A.push(a[j]);
if(A.size()==i)B.push(A.top()),A.pop();
}
r=q+1;
printf("%d\n",B.top());
A.push(B.top()),B.pop();
}
return 0;
}
P1168 中位数 [绿]
题目描述
给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \sim A_{2k - 1}\)的中位数。即前\(1,3,5,…\)个数的中位数。
试做
代码
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 100010
priority_queue<int,vector<int>>bq;
priority_queue<int,vector<int>,greater<>>sq;
int num[N];
int main(){
int t;cin>>t;
for(int i=1;i<=t;i++)cin>>num[i];
for(int i=1;i<=t;i++){
if(bq.empty()||num[i]<=bq.top())
bq.push(num[i]);
else sq.push(num[i]);
while (bq.size()-sq.size()>1||bq.size()-sq.size()>1)
if (bq.size()>sq.size()){
sq.push(bq.top());
bq.pop();
}
else{
bq.push(sq.top());
sq.pop();
}
if (i%2) cout<<(bq.size()>sq.size()?bq.top():sq.top())<<endl;
}
}
批改
\(A\)了。但是奇怪的是abs
函数用不了,说是冲突???为毛啊
他山之石
https://www.luogu.com.cn/blog/deco/solution-p1168
#include <bits/stdc++.h>
using namespace std;
int n;
vector<int>a;
int main()
{
cin>>n;
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);
a.insert(upper_bound(a.begin(),a.end(),x),x);//二分插入保证单调性
if(i%2==1)
{
printf("%d\n",a[(i-1)/2]);//是奇数个就输出
}
}
return 0;
}
emmmm好像是可以用upper_bound
过。。。
P2085 最小函数值 [绿]
题目描述
有 \(n\) 个函数,分别为 \(F_1,F_2,\dots,F_n\)。定义 \(F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb N*)\)。给定这些 \(A_i\)、\(B_i\) 和 \(C_i\),请求出所有函数的所有函数值中最小的 \(m\) 个(如有重复的要输出多个)。
试做
代码
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 100010
struct Node{
int id;
int dir;
int x;
int value;
bool operator>(const Node&node)const{
return value>node.value;
}
};
priority_queue<Node,vector<Node>,greater<>>q;
int a[N],b[N],c[N];
int cal(int aa,int bb,int cc,int x){
return aa*x*x+bb*x+cc;
}
int main(){
int n,m;cin>>n>>m;
int value,pos;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i];
pos=-1.0*b[i]/a[i]/2;
if(pos>0){
value=cal(a[i],b[i],c[i],pos);
q.push((Node){i,1,pos,value});
q.push((Node){i,-1,pos,value});
}
else q.push((Node){i,1,1,cal(a[i],b[i],c[i],1)});
}
int cnt=0;
while(cnt<m){
Node tmp=q.top();q.pop();
int x=tmp.x,d=tmp.dir,v=tmp.value,id=tmp.id;
if(x==0)continue;
cout<<v<<' ';cnt++;
if(x==1&&d<0)continue;
q.push((Node){id,1,x+d,cal(a[id],b[id],c[id],x+d)});
}
}
批改
分析
20220726,年轻人第一次秒掉绿题
不分析了。
P1631 序列合并 [绿]
题目描述
有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。
试做
代码
#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int a[N],b[N];
struct T{
int i,j;
long v;
bool operator>(const T&t)const{
return v>t.v;
}
};
priority_queue<T,vector<T>,greater<>>q;
int main(){
int t;cin>>t;
for(int i=1;i<=t;i++)cin>>a[i];
for(int i=1;i<=t;i++)cin>>b[i];
int cnt=0;
q.push((T){1,1,a[1]+b[1]});
while(cnt<t){
T temp=q.top();q.pop();
int i=temp.i,j=temp.j,v=temp.v;
cout<<v<<' ';
cnt++;
if(j+1<=t)q.push((T){i,j+1,a[i]+b[j+1]});
if(i+1<=t)q.push((T){i+1,j,a[i+1]+b[j]});
}
cout<<endl;
return 0;
}
批改
分析
这题其实比上一题还要简单。但是注意重复!!!
他山之石
从去重堆解摘选:
我们发现对于两个序列中的数,a[i]+b[j]<a[i+1]+b[j],a[i]+b[j]<a[i]+b[j+1];
并且很明显有a[1]+b[1]<a[2]+b[2]<……那么我们将其和(N个)压入优先队列。
只要有 i==j 的点被弹出,我们就把 a[i+1]+b[i] 与a[i]+b[i+1] 压入优先队列,
否则,当 i<j 时,将 a[i]+b[j+1] 压入即可; 当 i>j 时,将 a[i+1]+b[j] 压入。
为什么 i<j 时不压入 a[i+1]+b[j] ?我们考虑到此时 a[i+1]+ b[j-1] 及之前的数字可能依旧在优先队列中并且更优,就不必更新。
#26行之后改为以下代码可AC
if(i==j){
q.push((T){i+1,j,a[i+1]+b[j]});
q.push((T){i,j+1,a[i]+b[j+1]});
}
if(i>j&&i+1<=t)q.push((T){i+1,j,a[i+1]+b[j]});
if(i<j&&j+1<=t)q.push((T){i,j+1,a[i]+b[j+1]});
大佬们实在是太强了qwq
P4053 建筑抢修 [蓝]
暂留待做
标签:std,刷洛谷题,果子,int,namespace,数据结构,using,include 来源: https://www.cnblogs.com/shanzr/p/16519450.html