优先队列
区间分组:
左端点排序,维护每个分组的右端点,如果发现当前区间的左端点比最小的右端点大的话,那么可以把这个区间更新到当前组中;
[https://ac.nowcoder.com/acm/contest/22904/1010]
思路是优先把这个点放在所有右端点当前点大,并且左端点比较小的区间中,因为每个点都是逐渐增大,那么要尽可能用到比较左边的区间中,因为后面的点可能用不到这些区间,从而使后面的点可选择的区间多,也就是可扩展性强
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
struct T {
int l, r, v;
bool operator<(const T &t)const {
if (r != t.r) return r > t.r;
return l > t.l;
}
};
T a[N];
bool vis[N];
int main() {
int n, m;
while (cin >> n >> m) {
for (int i = 1; i <= n; i++) vis[i] = false;
priority_queue<T> q;
for (int i = 1; i <= m; i++) {
auto &[x, y, z] = a[i];
cin >> x >> y >> z;
q.push(a[i]);
}
int ans = 0;
//在r大于i的所有区间中优先选择l比较小的区间,这样的选择的扩展性最好
for (int i = 1; i <= n; i++) {
while (q.size() and q.top().r < i) q.pop();
if (q.size()) {
auto t = q.top();
vis[i] = true;
q.pop();
if (t.v - 1 > 0) {
q.push({t.l, t.r, t.v - 1});
}
}
}
for (int i = 1; i <= n; i++) if (vis[i]) ans++;
cout << ans << endl;
}
return 0;
}
tokitsukaze and Soldier
直观思路是枚举每个s[i],选择前s个最大的数字,这个操作可以使用优先队列来进行,按照s降序排序,每次把第i个加入队列中时,首先把队列中的数字减少到s-1个,每次肯定选择删掉最小的数字,然后把这个数字放进去,枚举过程中得出最大值
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
const int N=100010;
typedef pair<int,int> pii;
pii a[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
auto &[x,y]=a[i];
cin>>y>>x;
}
sort(a+1,a+1+n,greater<pii>());
priority_queue<int,vector<int>,greater<int>> q;
long long mmax=0,sum=0;
for(int i=1;i<=n;i++){
auto [x,y]=a[i];
while(q.size()>=x) sum-=q.top(),q.pop();
q.push(y),sum+=y;
mmax=max(mmax,sum);
}
cout<<mmax<<endl;
return 0;
}
[JSOI2007]建筑抢修
按照最后的修理时间排序,当发现当前的修理时间小于总时间时,可以尝试把已经修好的建筑中所花费时间最多的那个替换掉,如果能替换则替换;
#include <iostream>
#include <queue>
#include <algorithm>
#define int long long
using namespace std;
const int N=200010;
pair<int,int> a[N];
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) {
auto &[x,y]=a[i];
cin>>y>>x;
}
sort(a+1,a+1+n);
priority_queue<int> q;
int sum=0;
for(int i=1;i<=n;i++){
auto [y,x]=a[i];
if(sum+x>y){
auto t=q.top();
if(x<t) {
sum-=t;
q.pop();
sum+=x;
q.push(x);
}
}else q.push(x),sum+=x;
}
cout<<q.size()<<endl;
return 0;
}
第k小
序列只会逐渐增加,所以每次取第k个元素的时候不能从最小开始入手,可以考虑每次只维护一个大小为k的大根堆,每次删除掉比较大的元素,这些元素是一定不会对答案产生影响,所以可以删除
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
priority_queue<int> q;
int main(){
int n;
cin>>n;
int x;
int m,op;
cin>>m;
int k;
cin>>k;
for(int i=1;i<=n;i++) cin>>x,q.push(x);
while(m--){
cin>>op;
if(op&1){
cin>>x;
q.push(x);
}else {
while(q.size()>k) {q.pop();}
if(q.size()==k) cout<<q.top()<<endl;
else cout<<-1<<endl;
}
}
return 0;
}
一般这种题都是有一个权值和一个限制条件,假如是求最大那么应该是枚举每一个限制条件,找到满足限制条件的最大的哪些元素,所以可以按照限制条件排序,然后使用堆来维护