分析:
将编号为0,1,..k-1,的k个服务器开始空闲的时间记做time[t],0<=t<k,刚开始所有的time都为0
对于一个arrival[i],令x=i%k,我们想要知道以j= x,x+1,...,k-2,k-1,0,1,...,x-2,x-1顺序下的第一个j,使得time[j]<=arrival[i],可以发现[x,k-1]为一个连续的区间,[0,x-1]也是一个连续的区间。相当于在服务器中先查询[x,k-1]区间,如果找到直接返回;否则再查询[0,x-1]区间。这样就能够按照顺序查找到第一个空闲时间小于等于请求到达时间的空闲服务器,然后更新该服务器的空闲时间为arr[i]+load[i]。
注意:
因为线段树query的顺序是先左子树再右子树的,这样就能保证在区间[l,r]中首先查询到最左边的有效值。
class Solution {
public:
const static int N=1e5+5;
struct TreeNode{
int l,r,time; //l左边界,r右边界,从time时刻起 服务器开始空闲
}tr[4*N];
int cnt[N];
void pushup(int u){
tr[u].time= min(tr[u<<1].time,tr[u<<1|1].time);
}
void build(int u,int l,int r){
if(l==r){
tr[u]={l,r,0};
return;
}
tr[u].l=l,tr[u].r=r;
int mid=(l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
int query(int u,int l,int r,int time){ //查询以u为根节点,区间为[l,r],小于等于time的最小下标
if(l>r||tr[u].time>time) return -1;
if(tr[u].l==tr[u].r) {
if(tr[u].time<=time&&tr[u].l>=l&&tr[u].r<=r) return tr[u].l;
return -1;
}
int mid=(tr[u].l+tr[u].r)>>1;
if(r<=mid){
if(tr[u<<1].time<=time) return query(u<<1,l,r,time);
else return -1;
}else if(l>mid){
if(tr[u<<1|1].time<=time) return query(u<<1|1,l,r,time);
else return -1;
}else {
int ql=-1,qr=-1;
if(tr[u<<1].time<=time) ql= query(u<<1,l,r,time);
if(ql!=-1) return ql; //如果[tr[u].l,mid]中有符合条件的,那么它的下标一定比[mid+1,tr[u].r]的下标小,直接返回答案即可
if(tr[u<<1|1].time<=time) qr= query(u<<1|1,l,r,time);
return qr;
}
}
void update(int u,int i,int time){ //将下标为i的开始空闲的时间更新为time
if(tr[u].l==i&&tr[u].r==i){
tr[u].time=time;
return;
}
int mid=(tr[u].l+tr[u].r)>>1;
if(i<=mid) update(u<<1,i,time);
else update(u<<1|1,i,time);
pushup(u);
}
vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
int n=arrival.size(),maxx=0;
build(1,0,k-1);
int pos,t;
for(int i=0;i<n;i++){
pos=i%k;
t=query(1,pos,k-1,arrival[i]);
if(t==-1) t= query(1,0,pos-1,arrival[i]);
if(t!=-1){
update(1,t,arrival[i]+load[i]);
++cnt[t];
maxx=max(maxx,cnt[t]);
}
}
vector<int>ans;
for(int i=0;i<k;++i){
if(cnt[i]==maxx) ans.emplace_back(i);
}
return ans;
}
};