优先队列、并查集
题意:给你n个数,算出前i个数的中位数。
这题挺善良的,只让输出奇数个的时候的中位数,不然可太麻烦了,我们维护两个优先队列,一个代表小于中位数的值,一个代表大于中位数的值,这样中位数永远都是数量较大的那个队列的队首元素,但是添加的时候要注意,一次添加两个,不然偶数个的时候就不知道中位数是谁了,这也就是为什么我说这题还做了个人。添加完之后要记得维护一下这两个队列的大小关系,让他们的元素数量差不超过1,这样才能保证一定取到中位数。
这个做法叫对顶堆
#include <bits/stdc++.h>
using namespace std;
int x[10001];
priority_queue<int>le;
priority_queue<int,vector<int>,greater<int>>ri;
int main(){
int t;
cin>>t;
while(t--){
int n,num;
cin>>num>>n;
for(int i=0;i<n;i++){
cin>>x[i];
}
//清空一下,第一发mle,不知道是不是因为我两个队列设的是局部变量
while(le.size()!=0){
le.pop();
}
while(ri.size()!=0){
ri.pop();
}
le.push(x[0]);
int k=0;
cout<<num<<" "<<(n+1)/2<<endl;
int now=x[0];
//这里加等于是为了输出最后一轮的中位数,不然会少一个
for(int i=1;i<=n;i+=2){
if(le.size()>ri.size()){
now=le.top();
}else{
now=ri.top();
}
cout<<now<<" ";
if(i==n||i==n+1){
break;
}
//第二发wa在这:10个一行,但是最后一行如果满十个不用输出k
k++;
if(k%10==0){
cout<<endl;
}
if(x[i]>now){
ri.push(x[i]);
}else{
le.push(x[i]);
}
if(i==n-1){
break;
}
if(x[i+1]>now){
ri.push(x[i+1]);
}else{
le.push(x[i+1]);
}
while(fabs(le.size()-ri.size())>1){
if(le.size()>ri.size()){
int t=le.top();
le.pop();
ri.push(t);
}else{
int t=ri.top();
ri.pop();
le.push(t);
}
}
}
cout<<endl;
}
return 0;
}
tokitsukaze and Soldier (nowcoder.com)
一道贪心+优先队列,我们将士兵按照s从大到小排列,因为这样我可以选择的人数才是递增的,不像我从小到大排列会造成一上来就有n个可以选择的数的情况。然后每当我要加入一个人数比当前队伍人数少的士兵时,我将价值最小的几个士兵剔除,答案一定出现在其中某一步操作的时候。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
ll v,s;
bool operator<(const node&a)const{
return v>a.v;
}
};
node x[100001];
bool com(node a,node b){
if(a.s==b.s){
return a.v>b.v;
}
return a.s>b.s;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>x[i].v>>x[i].s;
}
sort(x,x+n,com);
// for(int i=0;i<n;i++){
// cout<<x[i].v<<" "<<x[i].s<<endl;
// }
priority_queue<node>q;
ll ans=0,now=0;
for(int i=0;i<n;i++){
q.push(x[i]);
now+=x[i].v;
while(q.size()>x[i].s){
now-=q.top().v;
q.pop();
}
ans=max(ans,now);
}
cout<<ans<<endl;
return 0;
}
感觉自己越学越菜了是怎么回事,这两场cf1300的题都a不出来,真是tfw.
和上一道一样的题,贪心+优先队列,感觉就是堆贪心啊,之前我练过,只不过有点忘了,做了上面那题想起来一点,就不放代码了,水题。
2300的题,雨巨真的是离谱,居然教我2300的题,但是这题看下来就是一个贪心+优先队列,但是数据很烦,我被卡常数了,艹。
题意:n台电脑,每台初始电量为ai,每秒消耗bi,问最少要一个功率多少的电源能让他们坚持k秒,但是一分钟内,电源只能给一个电脑充电。
思路:一看就是二分,然后每次判断的时候我们贪心地给当前看起来最坚持不下去的充电,看看能否坚持k秒。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
ll a,b,s;//这个s代表比值,我就是这里被卡了,前几发全都是判断a/b,但是有了这个就不用那么多次计算除法了,绝了。
bool operator<(const node m)const{
return s>m.s;
}
};
ll n,k;
node x[200001];
priority_queue<node>q0;
inline bool fun(ll m){
priority_queue<node>q(q0);
ll ans=0;
while(q.top().s<k-1){
if(q.top().s<ans){
return false;
}
node t=q.top();
q.pop();
t.a+=m;
t.s=t.a/t.b;
q.push(t);
ans++;
if(ans>k){
return false;
}
}
return true;
}
int main(){
std::ios::sync_with_stdio(false);
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++){
scanf("%lld",&x[i].a);
}
for(int i=0;i<n;i++){
scanf("%lld",&x[i].b);
}
for(int i=0;i<n;i++){
x[i].s=x[i].a/x[i].b;
q0.push(x[i]);
}
ll l=0,r=1e13;
ll ans=1e13+1;
while(l<=r){
ll mid=(l+r)/2;
if(fun(mid)){
ans=min(ans,mid);
r=mid-1;
}else{
l=mid+1;
}
}
if(ans==1e13+1){
printf("-1\n");
return 0;
}
printf("%lld\n",ans);
return 0;
}
给一个会动态增长的数组,求第k小的数
这题看上去好像不难,但是因为数据范围太大,要求插入和查询都不能超过O(logn)因此想到了用优先队列,这个队列里只存放前k个小的数,因为k个以后的数是不可能成为答案的,所以不用纪录他们。
#include <bits/stdc++.h>
using namespace std;
int x[200001];
int main(){
int n,m,k;
cin>>n>>m>>k;
for(int i=0;i<n;i++){
cin>>x[i];
}
priority_queue<int>q;
for(int i=0;i<n;i++){
if(q.size()<k){
q.push(x[i]);
}else{
if(q.top()>x[i]){
q.pop();
q.push(x[i]);
}
}
}
while(m--){
int ca;
cin>>ca;
if(ca==1){
int b;
cin>>b;
if(q.size()<k){
q.push(b);
}else{
if(q.top()>b){
q.push(b);
q.pop();
}
}
}else{
if(q.s