题目链接

3492. 负载均衡

思路一

我的思路是先按机器分类,然后对每个机器进行处理。

对于每个机器,有一个开始时间a和持续时间c,消耗算力d。可以维护一个时间区间,其权值为这段时间内所消耗的算力。

将m个查询转化为离线查询。如果这个机器能分配到资源,就进行区间修改,即对[a,a+c]区间的算力+d,具体可以用树状数组维护(差分,区间修改+单点查询)。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int tr[N],v[N];
void update(int i,int k,int n)
{
    while(i<=n)
    {
        tr[i]+=k;
        i+=i&(-i);
    }
}
int query(int i)
{
    int s=0;
    while(i)
    {
        s+=tr[i];
        i-=i&(-i);
    }
    return s;
}
struct node
{
    int id; // 原来的询问序号
    int a; // 开始时间
    int c; // 耗时
    int d; // 算力消耗
};
vector<node>g[N];
int ans[N],ta[N];
int main()
{
    ios::sync_with_stdio(false);
    int n,m,a,b,c,d;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i];
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c>>d;
        g[b].push_back({i,a,c,d}); // 按机器分类
    }
    for(int i=1;i<=n;i++)
    {
        if(g[i].size())
        {
            //memset(tr,0,sizeof(tr)); 会超时
            int sz=g[i].size();
            for(int j=0;j<sz+6;j++)
            {
                ta[j]=g[i][j].a;
                tr[j]=0; // for循环清空树状数组,用memset超时
            }
            for(int j=0;j<sz;j++)
            {
                node it=g[i][j];
                int id=it.id;
                a=it.a;c=it.c;d=it.d;
                int p=j+1; // 右移到1~g[i].size
                int rest=v[i]-query(p); // 还剩多少算力可用
                if(rest>=d)
                {
                    ans[id]=rest-d; // 记录答案
                    // 结束时间a+c
                    int k=lower_bound(ta+j,ta+sz,a+c)-ta; // <a+c的第一个位置(-1再+1)
                    update(p,d,sz+5);
                    update(k+1,-d,sz+5);
                }
                else ans[id]=-1; // 记录答案
            }
        }
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

自己想的这个思路,想复杂了,勉强能莽过去(1112 ms)。

思路二

从题解区看到的普遍做法。

对于每个机器维护一个堆,含有两个权值{结束时刻,消耗算力}。可以在线维护每个机器的算力变化,首先把当前机器的堆中的结束时刻小于当前机器开始时刻的全部弹出,同时恢复这些算力。 此后,如果能分配资源就直接把当前机器结束时刻和消耗算力加到堆中,否则输出-1即可。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q[N];
// 存每个机器的{结束时刻,消耗算力},按从小到大排序
int v[N];
int main()
{
    ios::sync_with_stdio(false);
    int n,m,a,b,c,d;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i];
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c>>d;
        while(!q[b].empty()&&q[b].top().first<a) // 把结束时刻小于a的所有任务弹出,恢复算力
        {
            v[b]+=q[b].top().second; // 恢复算力
            q[b].pop(); // 弹出
        }
        if(v[b]<d)printf("-1\n");
        else
        {
            q[b].push({a+c-1,d}); // 分配资源,实际上是持续到a+c-1而不是a+c
            v[b]-=d;
            printf("%d\n",v[b]);
        }
    }
    return 0;
}