洛谷-数据结构2-1-二叉堆与树状数组1

P3378 【模板】堆

题目描述

给定一个数列,初始为空,请支持下面三种操作:

  1. 给定一个整数 x,请将 x 加入到数列中。
  2. 输出数列中最小的数。
  3. 删除数列中最小的数(如果有多个数最小,只删除 1 个)。

输入格式

第一行是一个整数,表示操作的次数 n。

接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。

  • 若 op=1,则后面有一个整数 x,表示要将 x 加入数列。
  • 若 op=2,则表示要求输出数列中的最小数。
  • 若 op=3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。

输出格式

对于每个操作 2,输出一行一个整数表示答案。

输入输出样例

输入 #1复制

复制代码
5
1 2
1 5
2
3
2

输出 #1复制

复制代码
2
5

说明/提示

【数据规模与约定】

对于 30% 的数据,保证 n≤15。对于 70% 的数据,保证 n≤104。对于 100% 的数据,保证 1≤n≤106,1≤x<231,op∈{1,2,3}。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int>> st;
int main(){
	int n;
	cin>>n;
	while(n--){
		int m;
		cin>>m;
		if(m==1){
			int x;
			cin>>x;
			st.push(x);
		}
		else if(m==2){
			int y=st.top();
			cout<<y<<endl;
		}
		else{
			st.pop();
		}
	}
	return 0;
}

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

  1. a1​,a2​,⋯,am​:一串将要被放进 Black Box 的元素。例如上面的例子中 a=[3,1,−4,2,8,−1000,2]。

  2. u1​,u2​,⋯,un​:表示第 ui​ 个元素被放进了 Black Box 里后就出现一个 GET 命令。例如上面的例子中 u=[1,2,6,6] 。输入数据不用判错。

输入格式

第一行两个整数 m 和 n,表示元素的个数和 GET 命令的个数。

第二行共 m 个整数,从左至右第 i 个整数为 ai​,用空格隔开。

第三行共 n 个整数,从左至右第 i 个整数为 ui​,用空格隔开。

输出格式

输出 Black Box 根据命令串所得出的输出串,一个数字一行。

输入输出样例

输入 #1复制

复制代码
7 4
3 1 -4 2 8 -1000 2
1 2 6 6

输出 #1复制

复制代码
3
3
1
2

说明/提示

数据规模与约定
  • 对于 30% 的数据,1≤n,m≤104;

  • 对于 50% 的数据,1≤n,m≤105;

  • 对于 100% 的数据,1≤n,m≤2×105,∣ai​∣≤2×109,保证 u 序列单调不降。

实现代码:

cpp 复制代码
#include <cstdio>
#include <queue>
#define Qmax priority_queue<int>
#define Qmin priority_queue<int,vector<int>,greater<int> >
#define f(i , a , b) for(int i=(a) ; i <= (b) ; i++)
using namespace std;
inline int Input(){
    char C=getchar();
    int N=0 , F=1;
    while(('0' > C || C > '9') && (C != '-')) C=getchar();
    if(C == '-') F=-1 , C=getchar();
    while('0' <= C && C <= '9') N=(N << 1)+(N << 3)+(C - 48) , C=getchar();
    return F*N; 
} 
int main(){
    int a[200001];
    Qmax A;
    Qmin B;
    int n=Input() , m=Input() , r=1 , q;
    f(i , 1 , n) a[i]=Input();
    f(i , 1 , m){
        q=Input();
        f(j , r , q){
            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;
}

P1090 [NOIP 2004 提高组] 合并果子

题目背景

P6033 为本题加强版。

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 3 种果子,数目依次为 1,2,9。可以先将 1、2 堆合并,新堆数目为 3,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12,耗费体力为 12。所以多多总共耗费体力 =3+12=15。可以证明 15 为最小的体力耗费值。

输入格式

共两行。

第一行是一个整数 n(1≤n≤104),表示果子的种类数。

第二行包含 n 个整数,用空格分隔,第 i 个整数 ai​(1≤ai​≤2×104) 是第 i 种果子的数目。

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231。

输入输出样例

输入 #1复制

复制代码
3 
1 2 9 

输出 #1复制

复制代码
15

说明/提示

对于 30% 的数据,保证有 n≤103;

对于 50% 的数据,保证有 n≤5×103;

对于全部的数据,保证有 n≤104。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,sum;
priority_queue<int,vector<int>,greater<int>> st;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		st.push(x);
	}
	while(st.size()!=1){
		int a=st.top();
		st.pop();
		int b=st.top();
		st.pop();
		sum+=a+b;
		st.push(a+b);
	}
	cout<<sum;
	return 0;
}

P2168 [NOI2015] 荷马史诗

题目背景

追逐影子的人,自己就是影子 ------ 荷马

题目描述

Allison 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》 组成的鸿篇巨制《荷马史诗》实在是太长了,Allison 想通过一种编码方式使得它变得短一些。

一部《荷马史诗》中有 n 种不同的单词,从 1 到 n 进行编号。其中第 i 种单词出现的总次数为 wi​。Allison 想要用 k 进制串 si​ 来替换第 i 种单词,使得其满足如下要求:

对于任意的 1≤i,j≤n ,i=j ,都有:si​ 不是 sj​ 的前缀。

现在 Allison 想要知道,如何选择 si​,才能使替换以后得到的新的《荷马史诗》长度最小。在确保总长度最小的情况下,Allison 还想知道最长的 si​ 的最短长度是多少?

一个字符串被称为 k 进制字符串,当且仅当它的每个字符是 0 到 k−1 之间(包括 0 和 k−1 )的整数。

字符串 str1 被称为字符串 str2 的前缀,当且仅当:存在 1≤t≤m ,使得 str1=str2[1..t]。其中,m 是字符串 str2 的长度,str2[1..t] 表示 str2 的前 t 个字符组成的字符串。

输入格式

输入的第 1 行包含 2 个正整数 n,k ,中间用单个空格隔开,表示共有 n 种单词,需要使用 k 进制字符串进行替换。

接下来 n 行,第 i+1 行包含 1 个非负整数 wi​,表示第 i 种单词的出现次数。

输出格式

输出包括 2 行。

第 1 行输出 1 个整数,为《荷马史诗》经过重新编码以后的最短长度。

第 2 行输出 1 个整数,为保证最短总长度的情况下,最长字符串 si​ 的最短长度。

输入输出样例

输入 #1复制

复制代码
4 2
1
1
2
2

输出 #1复制

复制代码
12
2

输入 #2复制

复制代码
6 3
1
1
3
3
9
9

输出 #2复制

复制代码
36
3

说明/提示

【样例解释】
样例 1 解释

用 X(k) 表示 X 是以 k 进制表示的字符串。

一种最优方案:令 00(2) 替换第 1 种单词, 01(2) 替换第 2 种单词, 10(2) 替换第 3 种单词,11(2) 替换第 4 种单词。在这种方案下,编码以后的最短长度为:

1×2+1×2+2×2+2×2=12

最长字符串 si​ 的长度为 2 。

一种非最优方案:令 000(2) 替换第 1 种单词,001(2) 替换第 2 种单词,01(2) 替换第 3 种单词,1(2) 替换第 4 种单词。在这种方案下,编码以后的最短长度为:

1×3+1×3+2×2+2×1=12

最长字符串 si​ 的长度为 3 。与最优方案相比,文章的长度相同,但是最长字符串的长度更长一些。

样例 2 解释

一种最优方案:令 000(3) 替换第 1 种单词,001(3) 替换第 2 种单词,01(3) 替换第 3 种单词, 02(3) 替换第 4 种单词, 1(3) 替换第 5 种单词, 2(3) 替换第 6 种单词。

【数据规模与约定】

所有测试数据的范围和特点如下表所示(所有数据均满足 0<wi​≤1011):

测试点编号 n 的规模 k 的规模 备注
1 n=3 k=2
2 n=5 k=2
3 n=16 k=2 所有 wi​ 均相等
4 n=1000 k=2 wi​ 在取值范围内均匀随机
5 n=1000 k=2
6 n=100000 k=2
7 n=100000 k=2 所有 wi​ 均相等
8 n=100000 k=2
9 n=7 k=3
10 n=16 k=3 所有 wi​ 均相等
11 n=1001 k=3 所有 wi​ 均相等
12 n=99999 k=4 所有 wi​ 均相等
13 n=100000 k=4 所有 wi​ 均相等
14 n=100000 k=4 所有 wi​ 均相等
15 n=1000 k=5 所有 wi​ 均相等
16 n=100000 k=7 wi​ 在取值范围内均匀随机
17 n=100000 k=7
18 n=100000 k=8 wi​ 在取值范围内均匀随机
19 n=100000 k=9
20 n=100000 k=9
【提示】

选手请注意使用 64 位整数进行输入输出、存储和计算。

【评分方式】

对于每个测试点:

  • 若输出文件的第 1 行正确,得到该测试点 40% 的分数;
  • 若输出文件完全正确,得到该测试点 100% 的分数。

实现代码:

cpp 复制代码
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
struct node
{
    ll w,h;
    node(){w=0,h=0;}
    node(ll w,ll h):w(w),h(h){}
    bool operator <(const node &a)const{return a.w==w?h>a.h:w>a.w;}
};
ll ans;
priority_queue<node>q;
int main()
{
    ll n,k;ans=0;scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++)
    {
        ll w;scanf("%lld",&w);
        q.push(node(w,1));
    }
    while((q.size()-1)%(k-1)!=0)q.push(node(0,1));
    while(q.size()>=k)
    {
        ll h=-1;ll w=0;
        for(int i=1;i<=k;++i)
        {
            node t=q.top();q.pop();
            h=max(h,t.h);
            w+=t.w;
        }
        ans+=w;
        q.push(node(w,h+1));
    }
    printf("%lld\n%lld\n",ans,q.top().h-1);
    return 0;
}
相关推荐
挨踢ren1 小时前
C++虚函数:从基础到高阶
java·开发语言·jvm
hhb_6181 小时前
C语言核心技术难点梳理与实战案例解析
c语言·开发语言
海参崴-1 小时前
C++ STL篇 红黑树的模拟实现
开发语言·c++
Dshuishui1 小时前
我用 Claude Code 做了一个学术论文搜索工具
开发语言·人工智能·python·pip·uv
Resky08181 小时前
ReentrantReadWriteLock 深度解析
java·开发语言·juc
研究点啥好呢1 小时前
Momenta后端开发面试题精选:10道高频考题+答案解析(数据产线方向)
c++·python·面试·求职招聘
多加点辣也没关系1 小时前
数据结构与算法|第十七章:贪心算法
数据结构·算法·贪心算法
多加点辣也没关系1 小时前
数据结构与算法|第十四章:排序算法(上)— 比较类排序
数据结构·算法·排序算法
Hical612 小时前
C++26 前瞻心得:下一代 C++ 最值得期待的特性
c++