dtoj #2797. 旅行商

题解

网络流模板题。

考虑如何将一合法方案用流量图表示:

对于一个商人,从起点 s s s 连一条流量为 c i c_i ci,的边到 i i i,然后因为其商品可以在 a i → b i a_i \to b_i aibi 上分布,所以把 i i i a i → b i a_i \to b_i aibi 上每个点连一条流量无限的边。
最后对于树上的点,向汇点 t t t 连一条流量为 w i w_i wi 的边即可。
最后由于边数过大,用树剖+线段树优化建图即可。

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+10;
const LL INF=1e11;
int n,m,w[N];
int tot=1,ver[N<<5],fst[N<<5],nxt[N<<5],now[N<<5];
LL edge[N<<5];
inline void add(int x,int y,LL z){ver[++tot]=y;nxt[tot]=fst[x];fst[x]=tot;edge[tot]=z;}
vector<int> e[N];
int lt[N],d[N],rt[N],dfn,sz[N],son[N],ft[N],dep[N],maxn,top[N],idfn[N];
int s,t;
LL maxflow,ans;
void dfs_pre(int x,int fa){
	int mx=0;sz[x]=1;d[x]=d[fa]+1;ft[x]=fa;
	for(int i=0;i<(int)e[x].size();++i){
		int y=e[x][i];if(y==fa)continue;
		dfs_pre(y,x);sz[x]+=sz[y];
		if(sz[y]>sz[mx])mx=y;
	}
	son[x]=mx;
}
void dfs_get(int x,int fa){
	lt[x]=++dfn;idfn[dfn]=x;
	if(son[x])top[son[x]]=top[x],dfs_get(son[x],x);
	for(int i=0;i<(int)e[x].size();++i){
		int y=e[x][i];if(y==fa||y==son[x])continue;
		top[y]=y;dfs_get(y,x);
	}
	rt[x]=dfn;
}
inline int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(d[top[x]]>=d[top[y]])x=ft[top[x]];
		else y=ft[top[y]];
	}
	return (d[x]>=d[y])?y:x;
}
void build(int p,int l,int r){
	maxn=max(maxn,p+m);
	if(l==r){
		add(p+m,t,w[idfn[l]]);
		add(t,p+m,0);
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);build((p<<1)|1,mid+1,r);
	add(p+m,(p<<1)+m,INF);add((p<<1)+m,p+m,0);
	add(p+m,(p<<1)+m+1,INF);add((p<<1)+m+1,p+m,0);
}
void jia(int i,int p,int l,int r,int L,int R){
	if(l>=L&&r<=R){
		add(i,p+m,INF);
		add(p+m,i,0);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid)jia(i,p<<1,l,mid,L,R);
	if(R>mid)jia(i,(p<<1)|1,mid+1,r,L,R);
}
inline void cale(int i,int x,int y){
	while(top[x]!=top[y]){
		int F=top[x];
		jia(i,1,1,n,lt[F],lt[x]);
	//	cout<<"FAQ "<<lt[F]<<" "<<lt[x]<<" "<<x<<" "<<F<<endl;
		x=ft[F];
	}
	if(x!=y)jia(i,1,1,n,lt[y]+1,lt[x]);
}
queue<int> q;
inline bool bfs(){
	while(q.size())q.pop();
	for(int i=1;i<=maxn;++i)dep[i]=0;
	dep[t]=0;
	q.push(s);now[s]=fst[s];dep[s]=1;
	while(q.size()){
		int x=q.front();q.pop();
	//	cout<<"FAQ "<<x<<endl;
		for(int i=fst[x];i;i=nxt[i]){
			int y=ver[i];if(!edge[i]||dep[y])continue;
			dep[y]=dep[x]+1;
			q.push(y);now[y]=fst[y];
			if(y==t)return true;
		}
	}
	return false;
}
inline LL dinic(int x,LL flow){
	LL res=flow;
	if(x==t)return flow;
	for(int i=now[x];i;i=nxt[i]){
		now[x]=i;int y=ver[i];
		if(!edge[i]||dep[y]!=dep[x]+1)continue;
		LL k=dinic(y,min(edge[i],res));
		if(!k)dep[y]=0;
		res-=k;
		edge[i]-=k;
		edge[i^1]+=k;
		if(!res)break;
	}
	return flow-res;
}
int main(){
//	freopen("013.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)scanf("%d",&w[i]);
	for(int i=1;i<n;++i){
		int x,y;scanf("%d%d",&x,&y);
		e[x].push_back(y);e[y].push_back(x);
	}
	s=0;t=5*n+m+1;
	dfs_pre(1,0);top[1]=1;dfs_get(1,0);
	build(1,1,n);
//	cout<<"FAq "<<maxn<<" "<<tot<<endl;
	for(int i=1;i<=m;++i){
		int a,b,c;scanf("%d%d%d",&a,&b,&c);
		int k=LCA(a,b);
	//	cout<<"LCA "<<k<<endl;
		cale(i,a,k);cale(i,b,k);
		add(s,i,c);add(i,s,0);jia(i,1,1,n,lt[k],lt[k]);
	}
//	cout<<"FAQ "<<tot<<" "<<t<<endl;
	while(bfs()){
//		cout<<"Dinic "<<" "<<ans<<endl;
		ans+=dinic(s,INF);
	}
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值