堆的整理
这是关于堆知识的整理 堆是一个二叉树 维护的是一个有序的二叉树
堆中某个结点的值总是不大于或不小于其父结点的值;(大根堆与小根堆)
堆总是一棵完全二叉树。
priority_queue
priority_queue<int, vector
这边的greater其实是一个函数用来比较的!!
堆的stl可以改写
关于改写堆 priority_queue<node, vector<node>,cmp1> s;
struct cmp1{
bool operator()(node x,node y){
return x.h>y.h;//为啥是小根堆??
}
};
struct node{
int x,y,h;
node(int _x,int _y,int _h)
{
x=_x,y=_y,h=_h;
} // 不会写构造函数
bool operator < (const node &A )
{
return h<A.h;
}
}
这样就改写完成了一个堆
堆其实经常跟贪心结合起来因为堆维护数据的特点一般我们会选择维护一个满足条件:实现最优解的堆故一般和贪心挂钩!!
堆:

自己写主要就两个操作!一个up,一个down都为log的复杂度.
小根堆: 父节点的值小于或等于子节点的值
大根堆: 父节点的值大于或等于子节点的值

include <bits/stdc++.h>
using namespace std;
struct node{
int v;
int pos;
}a[100005],b[100005];
int s1[100005],s2[100005],n,m;
void up1(int x)
{
while(x>1&&a[x].v<a[x/2].v)
{
swap(a[x],a[x/2]);
s1[a[x].pos]=x;
s1[a[x/2].pos]=x/2;
x/=2;
}
}
void down1(int x)
{
while(x+x<=n)
{
int y=x+x;
if(a[y].v>a[y+1].v&&y+1<=n) y++;
if(a[x].v>a[y].v)
{
swap (a[x],a[y]);
s1[a[x].pos]=x;
s1[a[y].pos]=y;
x=y;
}else break;
}
}
void up2(int x)
{
while(x>1&&b[x].v>b[x/2].v)
{
swap(b[x],b[x/2]);
s2[b[x].pos]=x;
s2[b[x/2].pos]=x/2;
x/=2;
}
}
void down2(int x)
{
while(x+x<=n)
{
int y=x+x;
if(y+1<=n&&b[y].v<b[y+1].v) y++;
if(b[x].v<b[y].v)
{
swap(b[x],b[y]);
s2[b[x].pos]=x;
s2[b[y].pos]=y;
x=y;
}else {
break;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
s1[i]=i;
s2[i]=i;
a[i].v=100;
a[i].pos=i;
b[i].v=100;
b[i].pos=i;
}
for(int i=1;i<=m;i++)
{
int t;
cin>>t;
if(t==1)
{
int x,y;
cin>>x>>y;
a[s1[x]].v+=y;
b[s2[x]].v+=y;
up1(s1[x]);
up2(s2[x]);
down1(s1[x]);
down2(s2[x]);
}else {
cout<<b[1].v<<' '<<a[1].v<<'\n';
}
}
}
对顶堆动态维护一个中位数!
include <bits/stdc++.h>
using namespace std;
priority_queue
priority_queue<int,vector
int n;
int main()
{
cin>>n;
int x;
cin>>x;
b.push(x);
cout<<b.top()<<' ';
for(int i=2;i<=n;i++)
{
int x;
cin>>x;
//cout<<x;
//cout<<x<<endl;
if(x<=b.top())
{
b.push(x);
}else s.push(x);
int s1=b.size();
int s2=s.size();
// cout<<s1<<' '<<s2<<endl;
if(s1-s2>1)
{
int tt=b.top();
b.pop();
s.push(tt);
}else if(s1-s2<1)
{
int tt=s.top();
s.pop();
b.push(tt);
}
if(i&1)
cout<<b.top()<<' ';
}
}
例子

这道题就是一个标准堆+贪心每次拿出最小的两个合并后又放回去得到的值用ans+起来最终得到答案
#### *****堆还有一个极为特殊的结构对顶堆*****
作用:动态维护第 n 小/大的数
用一个大根堆记录前面的数 用一个小根堆去记录后面的数 前面堆的大小总比后面大1
这样就是一个中位数!!
int x; 大根堆的大小总比小根堆的大小大1!!!
scanf("%d",&x);
if(x>a.top()) b.push(x);
else a.push(x);
int len1=a.size(); //大根堆
int len2=b.size(); //小根堆
if(len1-len2>1)//大根堆更大
{
int z=a.top(); 把大根堆的一个元素拿出来放入小根堆里面
a.pop();
b.push(z);
}else if(len1-len2<1)
{
int z=b.top(); 把小根堆的元素拿出来放入大根堆里面
b.pop();
a.push(z);
}
if(i%2)
printf("%d\n",a.top());
#### 动态维护可变的第 i 小的数 思路特别特别好!!!!
for(i=1;i<=m;i++)
{
while(cnt<geta[i]) //满足geta[i]后有j 个
{
cnt++;
a.push(add[cnt]); //把新的数放进来排个序
b.push(a.top()); //把a中最大的加入b中
a.pop(); //a最大的放入b中只留下j个数
} //这样就动态维护了第j 个数 j也可变!!!
//把a中j个元素输出出去就得到了 前 j 小的数 j还是可变的
//动态维护了一个区间最小值!!!
cout<<b.top()<<'\n';//j++ 了
a.push(b.top());
b.pop();//a中此时能多维护一个数
}
#### 有意思的题目!!!新思路!!!!妙妙妙!!

这道题就挺有意思的说下思路 如果一个个去弄肯定很难那就先读第一个函数的前m个放入堆中然后后面的函数依次加入去比较 跑一个o(nm)的一个遍历即可!!!之后的函数就是一个个去迭代,看到符合条件的就替换掉了,直到不满足就结束进入下一个函数
因为小的不满足比他大的肯定也不满足!!所以结果就已经确定了!!
#### 也有一道有意思的题目!!!

因为是看最多能修复多少个故想想看肯定是按照消失顺序从小到大排序尽量修多点.然后最重要的是判断
long long cnt=0,t=0;
for(i=1;i<=n;i++)
{
t+=a[i].x; 总计修复所花费的时间是多少
b.push(a[i].x); 记录在里面去 贪心堆!!!
cnt++; 计数!!!
if(t>a[i].y) //如果不能修当前那个
{
cnt--;
t-=b.top(); //从里面去花费时间最多的
b.pop(); //修当前这个 反正就修尽量多且时间还少的方案!!!
} //如果当前方案是最多的可以判断出当前的就不修了呗!!
}
#### https://www.luogu.com.cn/problem/P3045 特别好的一道贪心题!!!! 一开始 都以为是背包了!!!特别像背包!!

浙公网安备 33010602011771号