【学习笔记】[省选联考 2023] 人员调度

我们尝试把一个复杂的问题尽量简单化。

每个时刻,无非出现三种情况:

1.1 1.1 1.1 新增一个员工或者撤掉一个员工

1.2 1.2 1.2 查询当前局面下的答案

这个修改操作看着就很亲切啊。只有新增和删除,那么每个员工就作用于一个询问区间,对于询问操作的序列,我们建立二叉树,那么一个询问的答案就是从它所对应的叶子节点到根的路径上所有员工组成的答案。这是个很具象化的转化。

接下来事实上只用考虑一件事:当新增和撤掉一个员工时,动态维护当前答案。这看着似乎和前面非常矛盾,但是不要忘了在线段树上做 D F S DFS DFS的过程中,是插入了一堆元素后紧接着就撤销其影响,因此是可以做的。

然后考虑怎么计算答案。我们希望找到一个 不依赖于将权值排序的维护方法考场上我就是用的依赖于权值的维护方法,看来还是感知不够。 事实上这样的工具是能被我们找到的,因为我们可以用树剖维护树上的各种信息。记 s i z u siz_u sizu u u u子树的大小, s u s_u su为当前状态下子树容纳员工的数目,显然在任意状态下应该满足 s u ≤ s i z u s_u\le siz_u susizu,并且我们能非常严格的说明这是充要的。于是自然而然引出这样的维护方式:设插入的员工为 x x x找到 x x x到根的路径上深度最大的满足 s i z y = s y siz_y=s_y sizy=sy的点,然后将 y y y子树内权值最小的点替换掉(如果权值更小),如果找不到那么直接插入就好了。回溯的时候就按顺序把上一步的影响撤销掉就好了。

然后是实现细节。用线段树和树剖维护子树大小, set \text{set} set维护节点上所有权值组成的集合,结合子树内 d f n dfn dfn序是一段区间不难实现查找。这道题就做完了,线段树真是万能的数据结构啊。

复杂度 O ( n log ⁡ 3 n ) O(n\log^3 n) O(nlog3n)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
int sid;
int n,K,m,pre[N],suf[N],rk[N];
vector<int>G[N];
struct node{
    int x,v;
}members[N];
vector<int>events[N<<2];
vector<pair<int,pair<int,int>>>back[N<<2];
multiset<int>nums[N];
void dfs(int p,int l,int r,int ql,int qr,int x){
    if(ql<=l&&r<=qr){
        events[p].pb(x);
        return;
    }
    int mid=l+r>>1;
    if(ql<=mid)dfs(p<<1,l,mid,ql,qr,x);
    if(mid<qr)dfs(p<<1|1,mid+1,r,ql,qr,x);
}
int dfn[N],num,sz[N],tp[N],son[N];
void dfs(int u){
    sz[u]=1;
    for(auto v:G[u]){
        dfs(v),sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void dfs(int u,int topf){
    dfn[u]=++num,rk[num]=u,tp[u]=topf;
    if(son[u])dfs(son[u],topf);
    for(auto v:G[u]){
        if(!dfn[v])dfs(v,v);
    }
}
int fa[N],szmin[N<<2],tag[N<<2],zeropos[N<<2],val[N<<2],topval[N<<2];
ll res;
void add(int p,int x){szmin[p]+=x,tag[p]+=x;}
void pushdown(int p){if(tag[p])add(p<<1,tag[p]),add(p<<1|1,tag[p]),tag[p]=0;}
void pushupsz(int p){
    szmin[p]=min(szmin[p<<1],szmin[p<<1|1]);
    if(szmin[p<<1|1]==szmin[p])zeropos[p]=zeropos[p<<1|1];
    else zeropos[p]=zeropos[p<<1];
}
void updatesz(int p,int l,int r,int ql,int qr,int x){
    if(ql<=l&&r<=qr){add(p,x);return;};
    int mid=l+r>>1;pushdown(p);
    if(ql<=mid)updatesz(p<<1,l,mid,ql,qr,x);
    if(mid<qr)updatesz(p<<1|1,mid+1,r,ql,qr,x);
    pushupsz(p);
}
void pushupval(int p){val[p]=topval[val[p<<1]]<topval[val[p<<1|1]]?val[p<<1]:val[p<<1|1];}
void updateval(int p,int l,int r,int x){
    if(l==r){topval[rk[l]]=*nums[rk[l]].begin(),val[p]=rk[l];return;};
    int mid=l+r>>1;
    x<=mid?updateval(p<<1,l,mid,x):updateval(p<<1|1,mid+1,r,x);
    pushupval(p);
}
void build(int p,int l,int r){
    if(l==r){
        zeropos[p]=rk[l],szmin[p]=sz[rk[l]];
        topval[rk[l]]=*nums[rk[l]].begin(),val[p]=rk[l];
        assert(topval[rk[l]]==inf);
        return;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid),build(p<<1|1,mid+1,r);
    pushupsz(p),pushupval(p);
}
pair<int,int> queryszmin(int p,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr)return {szmin[p],zeropos[p]};
    int mid=l+r>>1;pushdown(p);
    if(qr<=mid)return queryszmin(p<<1,l,mid,ql,qr);
    if(mid<ql)return queryszmin(p<<1|1,mid+1,r,ql,qr);
    pair<int,int>x=queryszmin(p<<1,l,mid,ql,qr),y=queryszmin(p<<1|1,mid+1,r,ql,qr);
    if(y.fi<=x.fi)return y;return x;
}
int queryvalmin(int p,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr)return val[p];
    int mid=l+r>>1;
    if(qr<=mid)return queryvalmin(p<<1,l,mid,ql,qr);
    if(mid<ql)return queryvalmin(p<<1|1,mid+1,r,ql,qr);
    int x=queryvalmin(p<<1,l,mid,ql,qr),y=queryvalmin(p<<1|1,mid+1,r,ql,qr);
    return topval[x]<topval[y]?x:y;
}
int findpos(int x){
    int fx=tp[x];
    while(x){
        pair<int,int>y=queryszmin(1,1,n,dfn[fx],dfn[x]);
        assert(y.fi>=0);
        if(y.fi==0)return y.se;
        x=fa[fx],fx=tp[x];
    }
    return -1;
}
void updatepath(int x,int y){
    int fx=tp[x];
    while(x){
        updatesz(1,1,n,dfn[fx],dfn[x],y);
        x=fa[fx],fx=tp[x];
    }
}
void ins(int x,int v){
    nums[x].insert(v);
    updatepath(x,-1);
    updateval(1,1,n,dfn[x]);
    res+=v;
}
void del(int x,int v){
    assert(nums[x].count(v));
    nums[x].erase(nums[x].find(v));
    assert(nums[x].size());
    updatepath(x,1);
    updateval(1,1,n,dfn[x]);
    res-=v;
}
void dfs(int p,int l,int r){
    for(auto pos:events[p]){
        int x=members[pos].x,v=members[pos].v;
        int y=findpos(x);
        if(y==-1){
            ins(x,v),back[p].pb(make_pair(x,make_pair(v,0)));
        }
        else{
            int z=queryvalmin(1,1,n,dfn[y],dfn[y]+sz[y]-1);
            assert(topval[z]==*nums[z].begin());
            if(topval[z]<v){
                back[p].pb(make_pair(x,make_pair(v,0)));
                back[p].pb(make_pair(z,make_pair(topval[z],1)));
                ins(x,v);
                del(z,topval[z]);
            }
        }
    }
    if(l==r){
        cout<<res;
        if(l==m)cout<<"\n";
        else cout<<" ";
    }
    else{
        int mid=l+r>>1;
        dfs(p<<1,l,mid);
        dfs(p<<1|1,mid+1,r);
    }
    reverse(back[p].begin(),back[p].end());
    for(auto pos:back[p]){
        int x=pos.fi,v=pos.se.fi,op=pos.se.se;
        if(op==1){
            ins(x,v);
        }
        else{
            del(x,v);
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>sid;
    cin>>n>>K>>m;
    for(int i=2;i<=n;i++){
        cin>>fa[i],G[fa[i]].pb(i);
    }
    memset(suf,-1,sizeof suf);
    for(int i=1;i<=K;i++){
        cin>>members[i].x>>members[i].v;
        pre[i]=0;
    }
    for(int i=1;i<=m;i++){
        int op,id;
        cin>>op;
        if(op==1){
            K++;
            cin>>members[K].x>>members[K].v;
            pre[K]=i;
        }
        else{
            cin>>id;
            suf[id]=i-1;
        }
    }
    for(int i=1;i<=K;i++){
        if(~suf[i])dfs(1,0,m,pre[i],suf[i],i);
        else dfs(1,0,m,pre[i],m,i);
    }
    dfs(1);dfs(1,1);
    for(int i=1;i<=n;i++)nums[i].insert(inf);
    build(1,1,n);
    dfs(1,0,m);
}
posted @ 2023-06-08 15:58  仰望星空的蚂蚁  阅读(38)  评论(0)    收藏  举报  来源