其他分享
首页 > 其他分享> > 刷洛谷题单【数据结构2-1】

刷洛谷题单【数据结构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\)了

他山之石

最高赞题解,讲了一些c++模板知识

优先队列与堆

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 $ 次询问,求出每一次询问的区间内数字的最大值。

image

用我自己话说,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、数值,入队。

算法第二部分:

  1. 取队头,存入答案队列,标记\(vis\),然后按\(id\)前后搜索是否因为这一对出队,出现了新的BG。如果有,入队。

  2. 之后继续取队头,如果取出的元素已经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();
    }
}

批改

image

分析

。。。不给下载样例我也不好分析,看题解罢

这篇题解跟我想得一模一样啊为什么我错了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 要处理一串命令。

命令只有两种:

记住:第 \(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\) 个。现在用两个整数数组来表示命令串:

  1. \(a_1,a_2,\cdots,a_m\):一串将要被放进 Black Box 的元素。例如上面的例子中 \(a=[3,1,-4,2,8,-1000,2]\)。
  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)});
    }
}

批改

image

分析

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;
}

批改

image

分析

这题其实比上一题还要简单。但是注意重复!!!

他山之石

去重堆解摘选:

我们发现对于两个序列中的数,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