套上线段树分治。
用模拟费用流进行理解。
若一个点加入的时候子树内汇点边没有流满,那么直接流就行了。
如果流的全是子树内的,那么直接看能否替换即可,否则有反向边,往上跳到第一个全是子树内的,替换掉即可。
加入一个点的时候:
- 找到父亲中第一个全是子树内的流,找不到,则直接流,顺便将祖先全部减去 1
- 找到,则替换掉子树内最小的一个员工,若不能替换就byebye,否则直接替换,顺便将上面减去 1,将删掉的员工上面加上 1。
我们需要维护子树内的最小值以及其位置,使用 dfn 线段树,叶子维护 multiset,上面维护最值。
我们有哪些操作需要撤销?直接记录所有对线段树的操作,对应回退即可。
#include<bits/stdc++.h>
using namespace std;
#define N 400005
#define ls u<<1
#define rs u<<1|1
#define ll long long
#define inf 0x7fffffff
#define pii pair<int,int>
int n,k,m;
int w[N],pos[N],adt[N],vis[N],siz[N],son[N],dfn[N],rnk[N],top[N],dad[N],tot;
basic_string<int> G[N],qry[N];
void dfs(int u){siz[u]=1;for(int v:G[u])dfs(v),siz[u]+=siz[v],son[u]=siz[son[u]]>siz[v]?son[u]:v;}
void dfs(int u,int t){
top[u]=t;rnk[dfn[u]=++tot]=u;
if(son[u])dfs(son[u],t);
for(int v:G[u])if(v^son[u])dfs(v,v);
}
void insert(int u,int l,int r,int L,int R,int id){
if(L<=l&&r<=R)return qry[u]+=id,void();
int m=(l+r)>>1;
if(m>=L)insert(ls,l,m,L,R,id);
if(m<R)insert(rs,m+1,r,L,R,id);
}
namespace sgt1{
pii tr[N<<2];
multiset<pii> p[N<<2];
void build(int u,int l,int r){
tr[u]={inf,0};
if(l==r)return;
int m=(l+r)>>1;build(ls,l,m),build(rs,m+1,r);
}
void upd(int u,int l,int r,int x,int id,int op){
if(l==r){
if(op==-1)p[u].erase({w[id],id});
else p[u].insert({w[id],id});
tr[u]=!p[u].size()?pii{inf,0}:*p[u].begin();
return;
}
int m=(l+r)>>1;
if(m>=x)upd(ls,l,m,x,id,op);
else upd(rs,m+1,r,x,id,op);
tr[u]=min(tr[ls],tr[rs]);
}
pii ask(int u,int l,int r,int L,int R){
if(L<=l&&r<=R)return tr[u];
int m=(l+r)>>1;pii ans={inf,0};
if(m>=L)ans=min(ans,ask(ls,l,m,L,R));
if(m<R)ans=min(ans,ask(rs,m+1,r,L,R));
return ans;
}
}
namespace sgt2{
int tr[N<<2],tag[N<<2];
void pull(int u){tr[u]=min(tr[ls],tr[rs]);}
void pushtag(int u,int x){tag[u]+=x;tr[u]+=x;}
void push(int u){if(tag[u])pushtag(ls,tag[u]),pushtag(rs,tag[u]),tag[u]=0;}
void build(int u,int l,int r){
if(l==r)return tr[u]=siz[rnk[l]],void();
int m=(l+r)>>1;
build(ls,l,m),build(rs,m+1,r);
pull(u);
}
void upd(int u,int l,int r,int L,int R,int x){
if(L<=l&&r<=R)return pushtag(u,x);
push(u);int m=(l+r)>>1;
if(m>=L)upd(ls,l,m,L,R,x);
if(m<R)upd(rs,m+1,r,L,R,x);
pull(u);
}
int ask2(int u,int l,int r){
if(l==r)return tr[u]?inf:rnk[l];
push(u);int m=(l+r)>>1;
return tr[rs]?ask2(ls,l,m):ask2(rs,m+1,r);
}
int ask(int u,int l,int r,int L,int R){
if(L<=l&&r<=R)return tr[u]?inf:ask2(u,l,r);
push(u);int m=(l+r)>>1,ans;
if(m<R&&(ans=ask(rs,m+1,r,L,R))!=inf)return ans;
if(m>=L)return ask(ls,l,m,L,R);
return inf;
}
}
int a[N],opt[N],t;ll ans;
inline void add(int id){
int u=pos[id];ans+=w[id];
sgt1::upd(1,1,n,dfn[pos[id]],id,1);
while(u)sgt2::upd(1,1,n,dfn[top[u]],dfn[u],-1),u=dad[top[u]];
}
inline void del(int id){
int u=pos[id];ans-=w[id];
sgt1::upd(1,1,n,dfn[pos[id]],id,-1);
while(u)sgt2::upd(1,1,n,dfn[top[u]],dfn[u],1),u=dad[top[u]];
}
void solve(int u,int l,int r){
int tt=t;
//add
for(int id:qry[u]){
int u=pos[id];
while(u&&sgt2::ask(1,1,n,dfn[top[u]],dfn[u])==inf)u=dad[top[u]];
if(!u)a[++t]=id,opt[t]=1,add(id);
else{
int x=sgt2::ask(1,1,n,dfn[top[u]],dfn[u]);
int tid=sgt1::ask(1,1,n,dfn[x],dfn[x]+siz[x]-1).second;
if(w[tid]<w[id])a[++t]=id,opt[t]=1,add(id),a[++t]=tid,opt[t]=-1,del(tid);
}
}
int m=(l+r)>>1;
if(l==r)printf("%lld ",ans);
else solve(ls,l,m),solve(rs,m+1,r);
//del
while(tt!=t){
if(opt[t]==1)del(a[t]);
else add(a[t]);
--t;
}
}
int main(){
//freopen("transfer3.in","r",stdin);
//freopen("transfer.out","w",stdout);
scanf("%d%d%d%d",&w[0],&n,&k,&m);
for(int v=2;v<=n;v++)scanf("%d",&dad[v]),G[dad[v]]+=v;//printf("%d %d\n",dad[v],v);
for(int i=1;i<=k;i++)scanf("%d%d",&pos[i],&w[i]);
dfs(1);dfs(1,1);sgt2::build(1,1,n);sgt1::build(1,1,n);
for(int i=1,op,x;i<=m;i++){
scanf("%d",&op);
if(op==1)++k,scanf("%d%d",&pos[k],&w[k]),adt[k]=i;
else scanf("%d",&x),vis[x]=1,insert(1,0,m,adt[x],i-1,x);//printf("add %d %d %d\n",adt[x],i-1,x);
}
for(int i=1;i<=k;i++)if(!vis[i])insert(1,0,m,adt[i],m,i);//printf("add %d %d %d\n",adt[i],m,i);
solve(1,0,m);
}
/*
212365 312130 212365 171257 171257 129289 73320
*/