【LOJ574】「LibreOJ NOI Round #2」黄金矿工【模拟费用流】【树链剖分】【线段树】

题目链接
【LOJ574】「LibreOJ NOI Round #2」黄金矿工【模拟费用流】【树链剖分】【线段树】_第1张图片
把一个u上的权为x的矿工和一个v上的权为y的矿工匹配了,相当于u出现了一个权为-x的黄金,v出现了一个权为-y的黄金。
每个点开个set,维护每个轻子树能爬上来的最大权的矿工和自己点上的矿工。当一条重链上的流量发生改变,只有这条链顶节点的父亲的set需要改变,删除原来的最大权矿工,加入现在的能爬上来的最大权矿工。需要树链剖分,在线段树上维护流量和最值。

#include
#include
#include
#include
#include
using namespace std;
const int N=100005,inf=0x7f7f7f7f;
int n,q,u,v,d,op,cnt,head[N],to[N*2],nxt[N*2],dd[N*2];
int idx,fa[N],siz[N],dep[N],dis[N],son[N],dfn[N],pos[N],top[N],bot[N];
long long ans;
pair<int,int> last[N];
void adde(int u,int v,int d){
	to[++cnt]=v;
	nxt[cnt]=head[u];
	dd[cnt]=d;
	head[u]=cnt;
}
void dfs(int u){
	siz[u]=1;
	int v;
	for(int i=head[u];i;i=nxt[i]){
		v=to[i];
		if(v!=fa[u]){
			fa[v]=u;
			dep[v]=dep[u]+1;
			dis[v]=dis[u]+dd[i];
			dfs(v);
			siz[u]+=siz[v];
			if(!son[u]||siz[son[u]]<siz[v]){
				son[u]=v;
			}
		}
	}
}
void dfs(int u,int tp){
	dfn[u]=++idx;
	pos[idx]=u;
	top[u]=tp;
	if(son[u]){
		dfs(son[u],tp);
		bot[u]=bot[son[u]];
	}else{
		last[tp]=make_pair(-inf,0);
		bot[u]=u;
	}
	int v;
	for(int i=head[u];i;i=nxt[i]){
		v=to[i];
		if(v!=fa[u]&&v!=son[u]){
			dfs(v,v);
		}
	}
}
struct tree1{
	int minn[N*4],tag[N*4];
	void pushdown(int o){
		if(tag[o]){
			tag[o*2]+=tag[o];
			minn[o*2]+=tag[o];
			tag[o*2+1]+=tag[o];
			minn[o*2+1]+=tag[o];
			tag[o]=0;
		}
	}
	void update(int o,int l,int r,int L,int R,int v){
		if(L<=l&&R>=r){
			tag[o]+=v;
			minn[o]+=v;
			return;
		}
		pushdown(o);
		int mid=(l+r)/2;
		if(L<=mid){
			update(o*2,l,mid,L,R,v);
		}
		if(R>mid){
			update(o*2+1,mid+1,r,L,R,v);
		}
		minn[o]=min(minn[o*2],minn[o*2+1]);
	}
	int queryl(int o,int l,int r,int L,int R){
		if(L>R||minn[o]>0){
			return -1;
		}
		int mid=(l+r)/2;
		if(L<=l&&R>=r){
			if(l==r){
				return l;
			}
			pushdown(o);
			int res=queryl(o*2,l,mid,L,R);
			if(res==-1){
				res=queryl(o*2+1,mid+1,r,L,R);
			}
			return res;
		}
		pushdown(o);
		int res=-1;
		if(L<=mid){
			res=queryl(o*2,l,mid,L,R);
		}
		if(R>mid&&res==-1){
			res=queryl(o*2+1,mid+1,r,L,R);
		}
		return res;
	}
	int queryr(int o,int l,int r,int L,int R){
		if(L>R||minn[o]>0){
			return -1;
		}
		int mid=(l+r)/2;
		if(L<=l&&R>=r){
			if(l==r){
				return l;
			}
			pushdown(o);
			int res=queryr(o*2+1,mid+1,r,L,R);
			if(res==-1){
				res=queryr(o*2,l,mid,L,R);
			}
			return res;
		}
		pushdown(o);
		int res=-1;
		if(R>mid){
			res=queryr(o*2+1,mid+1,r,L,R);
		}
		if(L<=mid&&res==-1){
			res=queryr(o*2,l,mid,L,R);
		}
		return res;
	}
}t1;
struct tree2{
	pair<int,int> maxn[N*4];
	set<pair<int,int> > st[N*4];
	void build(int o,int l,int r){
		if(l==r){
			maxn[o]=make_pair(-inf,0);
			return;
		}
		int mid=(l+r)/2;
		build(o*2,l,mid);
		build(o*2+1,mid+1,r);
		maxn[o]=max(maxn[o*2],maxn[o*2+1]);
	}
	void update(int o,int l,int r,int k,pair<int,int> pv,pair<int,int> v){
		if(l==r){
			if(pv.first!=-inf){
				st[o].erase(pv);
			}
			if(v.first!=-inf){
				st[o].insert(v);
			}
			if(st[o].empty()){
				maxn[o]=make_pair(-inf,pos[l]);
			}else{
				maxn[o]=*st[o].rbegin();
			}
			return;
		}
		int mid=(l+r)/2;
		if(k<=mid){
			update(o*2,l,mid,k,pv,v);
		}else{
			update(o*2+1,mid+1,r,k,pv,v);
		}
		maxn[o]=max(maxn[o*2],maxn[o*2+1]);
	}
	pair<int,int> query(int o,int l,int r,int L,int R){
		if(L==l&&R==r){
			return maxn[o];
		}
		int mid=(l+r)/2;
		if(R<=mid){
			return query(o*2,l,mid,L,R);
		}else if(L>mid){
			return query(o*2+1,mid+1,r,L,R);
		}else{
			return max(query(o*2,l,mid,L,mid),query(o*2+1,mid+1,r,mid+1,R));
		}
	}
}t2,t3;
void add(int u,int v,int d){
	while(top[u]!=top[v]){
		t1.update(1,1,n,dfn[top[u]],dfn[u],d);
		u=fa[top[u]];
	}
	if(u!=v){
		t1.update(1,1,n,dfn[v]+1,dfn[u],d);
	}
}
void modify(int u){
	int v,t;
	pair<int,int> tmp;
	while(fa[top[u]]){
		t=t1.queryl(1,1,n,dfn[top[u]]+1,dfn[bot[u]]);
		if(t==-1){
			v=bot[u];
		}else{
			v=pos[t-1];
		}
		if(t1.queryl(1,1,n,dfn[top[u]],dfn[top[u]])!=-1){
			tmp=make_pair(-inf,0);
		}else{
			tmp=t3.query(1,1,n,dfn[top[u]],dfn[v]);
		}
		t3.update(1,1,n,dfn[fa[top[u]]],last[top[u]],tmp);
		last[top[u]]=tmp;
		u=fa[top[u]];
	}
}
void update1(int u,int val){
	int v=u,t=u,tmp;
	while(t){
		tmp=t1.queryr(1,1,n,dfn[top[t]]+1,dfn[t]);
		if(tmp!=-1){
			v=pos[tmp];
			break;
		}else if(t1.queryl(1,1,n,dfn[top[t]],dfn[top[t]])!=-1){
			v=top[t];
			break;
		}
		t=fa[top[t]];
	}
	pair<int,int> res=t2.query(1,1,n,dfn[v],dfn[v]+siz[v]-1);
	if(res.first+val<0){
		t3.update(1,1,n,dfn[u],make_pair(-inf,0),make_pair(val,u));
		modify(u);
	}else{
		ans+=res.first+val;
		t2.update(1,1,n,dfn[u],make_pair(-inf,0),make_pair(-val,u));
		t2.update(1,1,n,dfn[res.second],make_pair(res.first,res.second),make_pair(-inf,0));
		t3.update(1,1,n,dfn[res.second],make_pair(-inf,0),make_pair(-res.first,res.second));
		add(u,1,-1);
		add(res.second,1,1);
		modify(u);
		modify(res.second);
	}
}
void update2(int u,int val){
	int v,t=u,tmp;
	pair<int,int> res=make_pair(-inf,0);
	while(t){
		tmp=t1.queryl(1,1,n,dfn[t]+1,dfn[bot[t]]);
		if(tmp==-1){
			v=bot[t];
		}else{
			v=pos[tmp-1];
		}
		res=max(res,t3.query(1,1,n,dfn[top[t]],dfn[v]));
		t=fa[top[t]];
	}
	if(res.first+val<0){
		t2.update(1,1,n,dfn[u],make_pair(-inf,0),make_pair(val,u));
	}else{
		ans+=res.first+val;
		t3.update(1,1,n,dfn[u],make_pair(-inf,0),make_pair(-val,u)); 
		t3.update(1,1,n,dfn[res.second],make_pair(res.first,res.second),make_pair(-inf,0));
		t2.update(1,1,n,dfn[res.second],make_pair(-inf,0),make_pair(-res.first,res.second));
		add(res.second,1,-1);
		add(u,1,1);
		modify(res.second);
		modify(u);
	}
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<n;i++){
		scanf("%d%d%d",&u,&v,&d);
		adde(u,v,d);
		adde(v,u,d);
	}
	dfs(1);
	dfs(1,1);
	t2.build(1,1,n);
	t3.build(1,1,n);
	while(q--){
		scanf("%d%d%d",&op,&u,&v);
		if(op==1){
			update1(u,v-dis[u]);
		}else{
			update2(u,v+dis[u]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

你可能感兴趣的:(树链剖分,线段树,模拟费用流)