题意:装箱问题。两种算法分别对应操作系统中的首次适应算法和最佳适应算法。然后给定你一个请求序列(顺序不可更改),然后问你在这两种不同的算法下,每种情况会各自申请多少个个大小为C的箱子?
思路:很明显就是想怎么用算法有优化这个两种模拟。
对于第一种操作:我们每次都需要找的是最前面的第一个大于当前ai的箱子,快速查询就那么几种方法,这里没有顺序,所以我们就有线段树维护区间最大值,然后既然要找最前面的,那么我每次就在符合条件的情况下,尽量往左子树走,否则才会往右子树去找。
第二种操作:每次找的是大于等于当前值的一个箱子,那我们就用一个set维护一下当前所有存在箱子的大小即可,如果当前都满足不了条件,再申请新的箱子。注意:因为箱子中可能出现重复的容量,所以我们这里用multiset不会自动去重的set维护。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
const int MAXN = 1e6 + 7;
const int N = 1e6;
int tree[MAXN<<2],a[MAXN],b[MAXN],n,c,cnt;
multiset<int>st;
multiset<int>::iterator it;
void pushup(int rt){ tree[rt] = max(tree[rt<<1],tree[rt<<1|1]); }
void build(int rt,int l,int r){
// tree[rt] = c;
if(l == r){
tree[rt] = c;
return ;
}
int mid = (l+r)>>1;
build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
pushup(rt);
}
void modify(int rt,int l,int r,int p,int val){
if(l == r){
tree[rt] -= val;
return ;
}
int mid = (l+r)>>1;
if(p <= mid) modify(rt<<1,l,mid,p,val);
else if(p > mid) modify(rt<<1|1,mid+1,r,p,val);
pushup(rt);
}
int query(int rt,int l,int r,int val){
if(l == r) return l;
int mid = (l+r)>>1;
int res;
if(tree[rt<<1] >= val)
res = query(rt<<1,l,mid,val);
else if(tree[rt<<1|1] >= val)//这里初始化都为c 而题目又保证了ai < c 所以可以这样写
res = query(rt<<1|1,mid+1,r,val);
return res;
}
signed main(){
int t;
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&c);
for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]);
build(1,1,n);
cnt = 0;
for(int i = 1;i <= n;i ++){
int p = query(1,1,n,a[i]);
if(p > cnt) cnt++;//查到的当前合法位置 大于cnt了 证明需要申请新的空间了。
modify(1,1,n,p,a[i]);
}
int ans = 0;
multiset<int> :: iterator it;
st.insert(c - a[1]);
for (int i = 2; i <= n; ++i) {
it = st.lower_bound(a[i]);
if (it == st.end()) {
st.insert(c - a[i]);
continue;
}
int v = *it - a[i];
st.erase(it);
st.insert(v);
}
printf("%lld %d\n",cnt,st.size());
st.clear();
}
return 0;
}