P3378 【模板】堆
题目描述
给定一个数列,初始为空,请支持下面三种操作:
- 给定一个整数 x,请将 x 加入到数列中。
- 输出数列中最小的数。
- 删除数列中最小的数(如果有多个数最小,只删除 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 个。现在用两个整数数组来表示命令串:
-
a1,a2,⋯,am:一串将要被放进 Black Box 的元素。例如上面的例子中 a=[3,1,−4,2,8,−1000,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;
}