【学习笔记】[省选联考 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); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529959.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(33)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示