1 条题解
-
0
自动搬运
来自洛谷,原作者为

vectorwyx
打 OI 就像心跳一样自然,退役就像去世一样必然。搬运于
2025-08-24 22:45:59,当前版本为作者最后更新于2023-04-11 10:45:44,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
由题意可知,最终调度方案里,对于同一部门的员工,只需要保留能力值最大的,其余员工不会产生贡献,我们视为他们被淘汰了。那么有一个简单的贪心:像树形 dp 那样自底向上维护每棵子树的最优调度策略。对于叶节点,最优策略就是保留这个结点上能力值最大的那位员工。对于一个非叶结点 ,我们先求出它的所有儿子对应子树的最优策略,然后挨个考察初始位置在 上的所有员工 ,如果 的整棵子树里还有结点空闲着,那就把员工 调度到这个空闲的结点上。否则,找出目前 子树中能力值最小的那位员工,比较他与员工 的能力值,如果员工 的能力值更大就淘汰掉这位员工,腾出位置,把员工 调度过去。这个贪心的正确性显然。
注意到由于是自底向上进行的,我们其实并不关心每个员工究竟调度到了哪个结点,只需要关心当前子树中有哪些员工幸存了下来。更进一步地,我们其实是对每个结点 维护了一个有序序列,把 所有儿子的序列合并起来再加上初始时位置在 的所有员工得到序列 ,令 为 序列的长度,把 按能力值降序排列后淘汰掉末尾的 个员工就得到了 对应的序列( 为 的子树大小),淘汰 个员工是因为我们必须保证幸存的员工数量不超过子树大小。这个过程显然可以用可并堆来实现,令员工数为 ,单次复杂度为 ,总复杂度为 ,可以获得 48 分,这里给出了代码实现(采用的是 pb_ds 库的配对堆)。
接下来的方向是想办法快速维护这个合并的过程。先来看看只加点怎么做,此时,对于一位在之前的合并过程中被淘汰的员工,他之后肯定也没有机会复活,所以只需要维护那些当前还没被淘汰的员工。换言之,边加点,边维护每个结点 的序列。我们称结点 是满的,当且仅当 的序列大小恰好等于 。假设现在要在结点 新加入一位员工 ,考虑他对于 的祖先的序列的影响。
第一种情况是, 到根结点的链上没有结点是满的,此时加入员工 后 的祖先结点在合并序列时一定不会发生超载的情况,所以不会有员工被淘汰。那么员工 对祖先的影响仅仅是在对应序列中多加了一位员工。
第二种情况是, 到根结点的链上存在一些结点是满的,那员工 的加入势必会引起淘汰的发生。假装模拟这个向上合并的过程,显然第一次淘汰会发生在 的祖先中距离 最近的那个满的结点,记为 。 的序列的大小恰为 ,我们找出这个序列中最末尾的员工 (也就是能力值最小的),如果员工 的能力值比员工 都要小,那他的旅途到这就戛然而止了, 会把 淘汰掉。而如果员工 的能力值大于等于员工 ,那么把员工 替换成员工 肯定更优,而且根据单调性,员工 没有在之后的合并过程中被淘汰,所以用 换掉 后 也一定不会被淘汰,所以我们根本不用再考虑后面的过程了。
那么现在的问题变成了,单点修改,查询 祖先中距离 最近的满的结点,查询子树最小值。再转化一下,初始时每个结点 有一个权值 ,然后每在 结点加一个新员工就会把 到根的链上的结点的权值 ,问祖先中最近的权值等于 的点。这其实就是链加链 ,树剖一下,复杂度是 。
现在我们搞出了只加点时的做法,由于询问并不强制在线,套一层线段树分治即可变删除为撤销,视 同阶的话则复杂度为 ,如果将链加链 改用静态 LCT 实现即可变为 。不过树剖的常数还是蛮小的,完全可过。
代码如下:
#include<bits/stdc++.h> namespace vectorwyx{ #define pii pair<int,int> #define fi first #define se second #define pb push_back #define eb emplace_back #define mk make_pair #define sml(x,y) (x=min(x,y)) #define big(x,y) (x=max(x,y)) #define ll long long #define uint unsigned #define ull unsigned long long #define umap unordered_map #define db double #define fo(i,x,y) for(int i=(x);i<=(y);++i) #define go(i,x,y) for(int i=(x);i>=(y);--i) #define ptc putchar #define gc getchar #define emp emplace #define re return #define co continue #define brk break #define HH (ptc('\n')) #define bctz __builtin_ctz #define bclz __builtin_clz #define bppc __builtin_popcount using namespace std; ll seed=chrono::system_clock::now().time_since_epoch().count(); mt19937 rnd(seed); inline int rm(int x,int y){return (int)(rnd()%(y-x+1ll)+x);} inline int read(){signed ch=getchar();int x=0,f=1;while(!isdigit(ch)){if(ch==(int)('-'))f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;} template<typename T> void out(T *a,int l,int r){fo(i,l,r) cout<<*(a+i)<<' ';puts("");} const int N=1e5+5,inf=1e9; struct People{ int x,v; People(){x=0,v=inf;} bool operator<(const People &y)const{re v<y.v;} bool operator>(const People &y)const{re v>y.v;} }a[N<<1]; vector<int> e[N]; int n,m,k,dfn[N],rk[N],ti,siz[N],son[N],Tid,top[N],lst[N<<1],fa[N]; void dfs1(int x){ siz[x]=1; int mx=0; for(int i:e[x]){ dfs1(i); siz[x]+=siz[i]; if(siz[i]>mx) mx=siz[i],son[x]=i; } } void dfs2(int x,int t){ top[x]=t; dfn[x]=++ti;rk[ti]=x; if(son[x]) dfs2(son[x],t); for(int i:e[x]) if(i!=son[x]) dfs2(i,i); } #define ls (x<<1) #define rs (x<<1|1) #define mid ((l+r)>>1) namespace DS1{//维护子树最小值 //auto cmp=[](int x,int y){re a[x]<a[y];}; //priority_queue<int,vector<int>,decltype(cmp)> pq[N]; struct qwq{ int id; qwq(){id=0;} qwq(int o){id=o;} bool operator<(const qwq &x)const{ if(a[id].v!=a[x.id].v) re a[id]<a[x.id]; re id<x.id; } }; set<qwq> pq[N]; int tr[N<<2]; void ps(int x){ if(a[tr[ls]]<a[tr[rs]]) tr[x]=tr[ls]; else tr[x]=tr[rs]; } void upd(int x,int l,int r,int aim,int k){ if(l==r){ tr[x]=k; re; } if(aim<=mid) upd(ls,l,mid,aim,k); else upd(rs,mid+1,r,aim,k); ps(x); } int ask(int x,int l,int r,int L,int R){ if(l>=L&&r<=R) re tr[x]; if(R<=mid) re ask(ls,l,mid,L,R); if(L>mid) re ask(rs,mid+1,r,L,R); int wl=ask(ls,l,mid,L,R); int wr=ask(rs,mid+1,r,L,R); if(a[wl]>a[wr]) re wr; re wl; } void ins(int id){ int x=a[id].x; pq[x].insert(qwq(id)); upd(1,1,n,dfn[x],(*pq[x].begin()).id); } void del(int id){ int x=a[id].x; // assert(id==pq[x].top().id); pq[x].erase(qwq(id)); if(pq[x].empty()) upd(1,1,n,dfn[x],0); else upd(1,1,n,dfn[x],(*pq[x].begin()).id); } } namespace DS2{ namespace Sgt{ struct Node{ int tag,mn; Node(){tag=0;mn=inf;} void upd(int x){tag+=x,mn+=x;} }tr[N<<2]; void ps(int x){tr[x].mn=min(tr[ls].mn,tr[rs].mn);} void pd(int x){ int &k=tr[x].tag; if(!k) re; tr[ls].upd(k); tr[rs].upd(k); k=0; } void build(int x,int l,int r){ if(l==r){ tr[x].mn=siz[rk[l]]; re; } build(ls,l,mid); build(rs,mid+1,r); ps(x); } void upd(int x,int l,int r,int L,int R,int k){ // if(x==1) printf("upd(%d,%d,%d,%d,%d,%d)\n",x,l,r,L,R,k); if(l>=L&&r<=R){ tr[x].upd(k); re; } pd(x); if(L<=mid) upd(ls,l,mid,L,R,k); if(R>mid) upd(rs,mid+1,r,L,R,k); ps(x); } int ask(int x,int l,int r,int L,int R){ // if(x==1) printf("Sgt::ask(%d,%d,%d,%d,%d)\n",x,l,r,L,R); if(l>=L&&r<=R) re tr[x].mn; pd(x); if(R<=mid) re ask(ls,l,mid,L,R); if(L>mid) re ask(rs,mid+1,r,L,R); re min(ask(ls,l,mid,L,R),ask(rs,mid+1,r,L,R)); } int get(int x,int l,int r,int L,int R){ // printf("get(%d,%d,%d,%d,%d)\n",x,l,r,L,R); if(l>=L&&r<=R){ if(tr[x].mn>0) re 0; while(l<r){ pd(x); if(tr[rs].mn==0) x=rs,l=mid+1; else x=ls,r=mid; } re rk[l]; } pd(x); if(R<=mid) re get(ls,l,mid,L,R); if(L>mid) re get(rs,mid+1,r,L,R); int w=get(rs,mid+1,r,L,R); if(w) re w; re get(ls,l,mid,L,R); } } void build(){ Sgt::build(1,1,n); } void upd(int x,int k){ while(x){ int y=top[x]; Sgt::upd(1,1,n,dfn[y],dfn[x],k); x=fa[y]; } } int ask(int x){ while(x){ int y=top[x]; if(Sgt::ask(1,1,n,dfn[y],dfn[x])>0){ x=fa[y]; co; } re Sgt::get(1,1,n,dfn[y],dfn[x]); } re 0; } } namespace Company{ struct Roll{ int id,loser; }stk[N<<1]; int top; ll ans; void hello_world(int id){//正式步入职业生涯 //Hello, world! // printf("hello_world(%d)!\n",id); ans+=a[id].v; DS1::ins(id); DS2::upd(a[id].x,-1); } void say_goodbye(int id){ // printf("say_goodbye(%d)!\n",id); ans-=a[id].v; DS1::del(id); DS2::upd(a[id].x,1); } bool gao(int id){//把员工 id 加进来 // printf("gao(%d)\n",id); int t=DS2::ask(a[id].x); if(!t){ ++top; stk[top].id=id,stk[top].loser=0;//岗位充足,没有产生下岗员工 hello_world(id); re 1; } //很遗憾,零和博弈,必定有输家 int me=DS1::ask(1,1,n,dfn[t],dfn[t]+siz[t]-1);//末位淘汰 if(a[me]<a[id]){//内卷斗兽场 ++top; stk[top].id=id,stk[top].loser=me; hello_world(id); say_goodbye(me); re 1; } re 0; } void roll(){ // puts("roll_back"); int x=stk[top].id,y=stk[top].loser;top--; say_goodbye(x); if(y) hello_world(y); } } ll ans[N]; namespace DS3{//时间线段树 vector<int> tr[N<<2]; void push(int x,int l,int r,int L,int R,int id){ // printf("push(%d,%d,%d,%d,%d,%d)\n",x,l,r,L,R,id); if(l>=L&&r<=R){ tr[x].pb(id); re; } if(L<=mid) push(ls,l,mid,L,R,id); if(R>mid) push(rs,mid+1,r,L,R,id); } void dfs(int x,int l,int r){ // printf("dfs(%d,%d,%d)\n",x,l,r); int ct=0; for(int i:tr[x]) ct+=Company::gao(i); if(l==r) ans[l]=Company::ans; else dfs(ls,l,mid),dfs(rs,mid+1,r); while(ct--) Company::roll(); } } void file(){ freopen("transfer.in","r",stdin); freopen("transfer.out","w",stdout); } signed main(){ // file(); cin>>Tid; cin>>n>>k>>m; fo(i,2,n) fa[i]=read(),e[fa[i]].pb(i); dfs1(1); dfs2(1,1); // cout<<"son:";out(son,1,n); // cout<<"dfn:";out(dfn,1,n); // cout<<"top:";out(top,1,n); DS2::build(); fo(i,1,k) a[i].x=read(),a[i].v=read(); fo(i,1,m){ int o=read(),x=read(); if(o==1){ a[++k].x=x; lst[k]=i; a[k].v=read(); }else DS3::push(1,0,m,lst[x],i-1,x),lst[x]=-1; } fo(i,1,k) if(lst[i]>-1) DS3::push(1,0,m,lst[i],m,i); DS3::dfs(1,0,m); out(ans,0,m); return 0; } } /* ------------------------------------------------- */ signed main(){re vectorwyx::main();}
- 1
信息
- ID
- 8545
- 时间
- 5000ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者