题解 P3128 【[USACO15DEC]最大流Max Flow】

此类型题目有两种比较常见的做法:树链剖分和树上差分。

本题有多组修改一组询问,因此树上差分会比树链剖分优秀很多。

这里两种方法都进行介绍。


树链剖分和树上差分的本质都是将一颗树转换为一个区间,然后进行操作。

也就是说,先将一颗树变成区间,然后套用线段树/树状数组和差分。


树链剖分的具体流程不多加叙述,可以自己去翻它的模板题。

本题维护区间修改单点查询就OK了,最后输出答案的时候直接遍历所有节点取最大值。

1956ms,9805kb

#include<iostream>
#include<cstdio>
#include<algorithm>
#define qwq int
#define re register
using namespace std;
namespace Solve{
	#define N 200200
	qwq n,m;
	qwq cnt;
	qwq head[N];
	struct node{
		qwq to,next;
	}edge[N<<1];
	inline void add(qwq a,qwq b){
		edge[++cnt].to=b,edge[cnt].next=head[a],head[a]=cnt;
	}
	qwq dfn;
	qwq dep[N],fa[N],size[N],son[N],top[N],id[N];
	inline void dfs1(qwq now,qwq father,qwq deep){
		dep[now]=deep,fa[now]=father,size[now]=1;qwq max_son=-1;
		for(re qwq i=head[now];i;i=edge[i].next){
			qwq to=edge[i].to;
			if(to==father)continue;
			dfs1(to,now,deep+1);
			size[now]+=size[to];
			if(size[to]>max_son)max_son=size[to],son[now]=to;
		}
	}
	inline void dfs2(qwq now,qwq topf){
		id[now]=++dfn,top[now]=topf;
		if(!son[now])return;
		dfs2(son[now],topf);
		for(re qwq i=head[now];i;i=edge[i].next){
			qwq to=edge[i].to;
			if(to==son[now]||to==fa[now])continue;
			dfs2(to,to);
		}
	}
	struct tnode{
		qwq l,r,val,tag;
	}tree[N<<2];
	inline void pushup(qwq pos){
		tree[pos].val=tree[pos<<1].val+tree[pos<<1|1].val;
	}
	inline void pushdown(qwq pos){
		if(tree[pos].tag){
			tree[pos<<1].tag+=tree[pos].tag,tree[pos<<1|1].tag+=tree[pos].tag;
			tree[pos<<1].val+=tree[pos].tag*(tree[pos<<1].r-tree[pos<<1].l+1);
			tree[pos<<1|1].val+=tree[pos].tag*(tree[pos<<1|1].r-tree[pos<<1|1].l+1);
			tree[pos].tag=0;
		}
	}
	inline void build(qwq l,qwq r,qwq pos){
		tree[pos].l=l,tree[pos].r=r;
		if(l==r){
			tree[pos].val=0;
			return;
		}
		qwq mid=(l+r)>>1;
		build(l,mid,pos<<1),build(mid+1,r,pos<<1|1);
		pushup(pos);
	}
	inline void update(qwq l,qwq r,qwq v,qwq pos){
		if(l<=tree[pos].l&&tree[pos].r<=r){
			tree[pos].val+=v*(tree[pos].r-tree[pos].l+1);
			tree[pos].tag+=v;
			return;
		}
		pushdown(pos);
		qwq mid=(tree[pos].l+tree[pos].r)>>1;
		if(l<=mid)update(l,r,v,pos<<1);
		if(mid<r)update(l,r,v,pos<<1|1);
		pushup(pos);
	}
	inline qwq query(qwq l,qwq r,qwq pos){
		if(l<=tree[pos].l&&tree[pos].r<=r){
			return tree[pos].val;
		}
		pushdown(pos);
		qwq mid=(tree[pos].l+tree[pos].r)>>1,ans=0;
		if(l<=mid)ans=max(ans,query(l,r,pos<<1));
		if(mid<r)ans=max(ans,query(l,r,pos<<1|1));
		return ans;
	}
	inline qwq queryRange(qwq x,qwq y){
		qwq ans=0;
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			ans=max(ans,query(id[top[x]],id[x],1));
			x=fa[top[x]];
		}
		if(dep[x]>dep[y])swap(x,y);
		ans=max(ans,query(id[x],id[y],1));
		return ans;
	}
	inline void updateRange(qwq x,qwq y,qwq v){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			update(id[top[x]],id[x],v,1);
			x=fa[top[x]];
		}
		if(dep[x]>dep[y])swap(x,y);
		update(id[x],id[y],v,1);
	}
	inline void solve(){
		scanf("%d%d",&n,&m);
		for(re qwq i=1;i<=n-1;++i){
			qwq x,y;
			scanf("%d%d",&x,&y);
			add(x,y),add(y,x);
		}
		dfs1(1,1,1);
		dfs2(1,1);
		build(1,n,1);
		while(m--){
			qwq x,y;
			scanf("%d%d",&x,&y);
			updateRange(x,y,1);
		}
		qwq ans=0;
		for(re qwq i=1;i<=n;++i)ans=max(ans,query(id[i],id[i],1));
		cout<<ans;
	}
}
using namespace Solve;
qwq main(){
	solve();
}


很明显,树链剖分码量比较高,在赛场上码力不足的选手容易陷入调试的巨坑,因此还是首推码量小并且(对于本题而言)速度快的树上差分。

需注意的是:树上差分适用于多组修改单组询问的题目,其他情况会T上天。

对于要更新的两个点而言,我们只要在他们上面各标记+1,然后在他们的lca和lca的父亲上各标记-1即可。

然后在询问的时候跑一下dfs统计一下就出答案了。

(lca用倍增,当然tarjan树剖都行,反正别裸的直接套上去)

懒得打代码了。光速逃

posted @ 2018-10-25 16:17  Ilverene  阅读(136)  评论(0编辑  收藏  举报