• 题解 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树剖都行,反正别裸的直接套上去)

    懒得打代码了。光速逃

  • 相关阅读:
    迎战智能手机时代,芯片厂商谁能笑到最后?
    古老Ubuntu 安装 EDB 的奇葩旅程
    今天做了一个很糟糕的storage and buffer manager
    android 数据库SQL 框架例子
    关于VS2008编译错误"error LNK2005: 已经在 .obj 中定义"
    ListActivity源码分析
    最优非对称加密填充(OAEP)
    EVP_DigestInit(3)源码解析
    Java EE学习笔记(1:Servlet & JSP)
    Servlet
  • 原文地址:https://www.cnblogs.com/ilverene/p/9850597.html
Copyright © 2020-2023  润新知