P9168 [省选联考 2023] 人员调度
_jimmywang_ · · 题解
Extracted from 联合省选记录
-
P9168 [省选联考 2023] 人员调度
我和贪心势不两立!
删除操作很恶心,先不管。我们先来看不带删除怎么做。
定义一个员工为
考虑对每个点
这样就满足了存在一种题目中的分配(下放)方式,使得所有点上的员工数
相当于不考虑一个方案中没有贡献的点,而根据最优性,题目的一个最优方案一定能和一组
设
根据 Hall 定理,一个方案满足条件的充要条件是
证明感性理解,大概是每个员工子集都拆成不相交子树,然后在一个子树内限制尽量紧,于是把子树内所有的员工算上也要满足。具体不证了(要的话评论加一下)。
记
考虑加入一个员工
如果直接加入这个员工合法,那么由于加入它会使
但是如果
那我们如果要加入这个员工,必定要踢掉一个。我们找到
于是我们查询
至此,不带删的东西做完了。
至于具体实现,我们树剖维护 multiset,记录员工的能力值集合。即 multiset 中的最小值(就是员工最小能力值),查询子树内员工最小值即为查询区间内 multiset 上加数或删数,此时单个点的
现复杂度
好好好,带删怎么办?
离线后发现每个员工的存在时间都是一段区间,果断时间轴上分治,就可以将删除改为撤销。同时我们在树剖上的操作正好都是可撤销的(加 / 减和加入 / 删除),因此完美适配。
至此,我们在
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define f(i,a,b) for(ll i=a;i<=b;i++)
inline ll rd(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return x*f;
}
#define d rd()
ll Sid;
ll n,m,k;
//树剖
ll dfn[100010],sz[100010],son[100010];
ll de[100010],fa[100010],top[100010],rk[100010];
vector<ll>e[100010];
void dfs1(ll u,ll f){
de[u]=de[f]+1,fa[u]=f;sz[u]=1;
for(int i=0;i<e[u].size();i++){
ll v=e[u][i];if(v==f)continue;
dfs1(v,u);sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}ll TIm;
void dfs2(ll u,ll tp){
top[u]=tp;dfn[u]=++TIm;rk[TIm]=u;
if(son[u])dfs2(son[u],tp);
for(int i=0;i<e[u].size();i++){
ll v=e[u][i];if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
ll t[100010<<2];//min(size-ssum of empolees)
ll tid[100010<<2];//id of min
ll tg[100010<<2];//tag of t
multiset<ll>st[100010];
ll w[100010<<2];//min value of employees
ll wid[100010<<2];//id of min w
void upd(ll p){
if(t[rs(p)]<=t[ls(p)])t[p]=t[rs(p)],tid[p]=tid[rs(p)];
else t[p]=t[ls(p)],tid[p]=tid[ls(p)];
if(w[rs(p)]<=w[ls(p)])w[p]=w[rs(p)],wid[p]=wid[rs(p)];
else w[p]=w[ls(p)],wid[p]=wid[ls(p)];
}
void build(ll l,ll r,ll p){
if(l==r){t[p]=sz[rk[l]];w[p]=0x3f3f3f3f3f3f3f3f;tid[p]=wid[p]=l;return;}
ll mid=l+r>>1;
build(l,mid,ls(p));
build(mid+1,r,rs(p));
upd(p);
}
void pd(ll p){
if(!tg[p])return;
t[ls(p)]+=tg[p],t[rs(p)]+=tg[p];
tg[ls(p)]+=tg[p],tg[rs(p)]+=tg[p];
tg[p]=0;
}
void add(ll l,ll r,ll p,ll L,ll R,ll k){
if(L<=l&&r<=R){t[p]+=k,tg[p]+=k;return;}
pd(p);ll mid=l+r>>1;
if(mid>=L)add(l,mid,ls(p),L,R,k);
if(mid<R)add(mid+1,r,rs(p),L,R,k);
upd(p);
}
void ch(ll l,ll r,ll p,ll pos,ll k,ll o){
if(l==r){
if(o==0)st[l].insert(k);
else st[l].erase(st[l].find(k));
if(st[l].size())w[p]=*st[l].begin();
else w[p]=0x3f3f3f3f3f3f3f3f;return;
}pd(p);ll mid=l+r>>1;
if(mid>=pos)ch(l,mid,ls(p),pos,k,o);
if(mid<pos)ch(mid+1,r,rs(p),pos,k,o);
upd(p);
}inline pair<ll,ll> Min(pair<ll,ll> a,pair<ll,ll>b){
if(a.first!=b.first)return min(a,b);
if(a.second>b.second)return a;return b;
}
pair<ll,ll> mnt(ll l,ll r,ll p,ll L,ll R){
if(L<=l&&r<=R)return {t[p],tid[p]};
pd(p);ll mid=l+r>>1;pair<ll,ll>ans={0x3f3f3f3f,0x3f3f3f3f};
if(mid>=L)ans=Min(ans,mnt(l,mid,ls(p),L,R));
if(mid<R)ans=Min(ans,mnt(mid+1,r,rs(p),L,R));
return ans;
}pair<ll,ll> mnw(ll l,ll r,ll p,ll L,ll R){
if(L<=l&&r<=R)return {w[p],wid[p]};
pd(p);ll mid=l+r>>1;pair<ll,ll>ans={0x3f3f3f3f,0x3f3f3f3f};
if(mid>=L)ans=Min(ans,mnw(l,mid,ls(p),L,R));
if(mid<R)ans=Min(ans,mnw(mid+1,r,rs(p),L,R));
return ans;
}
void addch(ll u,ll v,ll k){
while(top[u]!=top[v]){
if(de[top[u]]<de[top[v]])swap(u,v);
add(1,n,1,dfn[top[u]],dfn[u],k);
u=fa[top[u]];
}if(dfn[u]>dfn[v])swap(u,v);
add(1,n,1,dfn[u],dfn[v],k);
}
pair<ll,ll> chmnt(ll u,ll v){
pair<ll,ll>ans={0x3f3f3f3f,0x3f3f3f3f};
while(top[u]!=top[v]){
if(de[top[u]]<de[top[v]])swap(u,v);
ans=Min(ans,mnt(1,n,1,dfn[top[u]],dfn[u]));
u=fa[top[u]];
}if(dfn[u]>dfn[v])swap(u,v);
ans=Min(ans,mnt(1,n,1,dfn[u],dfn[v]));
return ans;
}
pair<ll,ll> mnw(ll u){
return mnw(1,n,1,dfn[u],dfn[u]+sz[u]-1);
}ll SUM;
struct node{
ll u,x;
ll isf,iss;
ll pos;
ll delf,dels;
};
//加入员工
void addemp(node &p,ll u,ll x){
p.u=u,p.x=x;
// cout<<"----------------\nadd ("<<u<<","<<x<<")\n";
pair<ll,ll>is=chmnt(1,u);
p.isf=is.first,p.iss=is.second;
if(is.first!=0){
// cout<<"all ancectors are unfull.\n";
SUM+=x;ch(1,n,1,dfn[u],x,0);addch(1,u,-1);
return;
}ll pos=rk[is.second];p.pos=pos;
// cout<<"nearst full ancestor is "<<pos<<endl;
pair<ll,ll>del=mnw(pos);
p.delf=del.first,p.dels=del.second;
if(del.first>x)return;
// cout<<"min value of emploees: ("<<rk[del.second]<<","<<del.first<<")\n";
if(del.first)addch(1,rk[del.second],1),ch(1,n,1,del.second,del.first,1);
SUM-=del.first;SUM+=x;ch(1,n,1,dfn[u],x,0);
addch(1,u,-1);
}
ll U[200010],X[200010];
struct ask{ll o,x,y;}q[100010];
//线段树分治
vector<ll>tr[100010<<2];
ll Ol[200010],Or[200010];
void ADD(ll l,ll r,ll p,ll L,ll R,ll o){
if(L<=l&&r<=R){tr[p].push_back(o);return;}
ll mid=l+r>>1;if(mid>=L)ADD(l,mid,ls(p),L,R,o);
if(mid<R)ADD(mid+1,r,rs(p),L,R,o);
}
void get(ll l,ll r,ll p){
vector<node>del;del.clear();
for(int j=0;j<tr[p].size();j++){
// cout<<l<<" "<<r<<" "<<p<<" ";
ll i=tr[p][j];ll u=U[i],x=X[i];
// cout<<"("<<u<<","<<x<<")\n";
node o={0,0,0,0,0,0,0};addemp(o,u,x);
del.push_back(o);
}
if(l==r)printf("%lld ",SUM);
ll mid=l+r>>1;if(l!=r)get(l,mid,ls(p)),get(mid+1,r,rs(p));
for(int i=del.size()-1;i>=0;i--){
node o=del[i];ll u=o.u,x=o.x;
if(o.isf!=0){
addch(1,u,1);ch(1,n,1,dfn[u],x,1);SUM-=x;
}else if(o.delf>x)continue;
else{
addch(1,u,1);
SUM+=o.delf;SUM-=x;ch(1,n,1,dfn[u],x,1);
if(o.delf)addch(1,rk[o.dels],-1),ch(1,n,1,o.dels,o.delf,0);
}
}
}
int main(){
// freopen("transfer15.in","r",stdin);
// freopen("transfer.out","w",stdout);
Sid=d;n=d,k=d,m=d+1;
f(i,2,n)e[d].push_back(i);
dfs1(1,0),dfs2(1,1);build(1,n,1);
f(i,1,k)Ol[i]=1,Or[i]=m,U[i]=d,X[i]=d;
f(i,2,m){
ll o=d;if(o==1){
ll u=d,x=d;q[i]={o,u,x};k++;
U[k]=u,X[k]=x,Ol[k]=i,Or[k]=m;
}else{
ll x=d;q[i]={o,x,0};
Or[x]=i-1;
}
}
f(i,1,k)ADD(1,m,1,Ol[i],Or[i],i);
get(1,m,1);
return 0;
}