CodeForces 903G Yet Another Maxflow Problem 题解

本文探讨了一种解决二分图中最大流问题的算法,通过转化为最小割问题进行求解。介绍了如何利用线段树维护边的流量,并在修改操作下快速更新最大流,适用于动态变化的图结构。

题目传送门

题目大意: 有一张二分图,左边有 n n n 个点组成集合 A A A,右边有 n n n 个点组成集合 B B B A , B A,B A,B 满足 A i A_i Ai A i + 1 A_{i+1} Ai+1 连边, B i B_i Bi B i + 1 B_{i+1} Bi+1 连边,流量都给出,然后还有 m m m A i A_i Ai 连向 B j B_j Bj 的边,流量也给出,最后还有 q q q 组修改,每次修改 A x A_x Ax A x + 1 A_{x+1} Ax+1 的流量,要求每次修改后输出最大流。

题解

因为最大流等于最小割,所以这题可以转化为求最小割。

为了方便,设连接集合 A A A 里面的点的边的集合为 E A EA EA,连接集合 B B B 里面的点的边的集合为 E B EB EB,连接集合 A A A 和集合 B B B 里面的点的集合为 E C EC EC

假如使 E A x EA_x EAx E B y EB_y EBy 成为割边,那么要使得 A 1 A_1 A1 B n B_n Bn 不连通,那么需要把所有 E C ( i , j )    ( 1 ≤ i < x , y < j ≤ n ) EC_{(i,j)}~~(1\leq i <x,y<j\leq n) EC(i,j)  (1i<x,y<jn) 都给割掉。

a n s ( x , y ) ans(x,y) ans(x,y) 表示使 E A x EA_x EAx E B y EB_y EBy 成为割边的贡献,那么显然有:
a n s ( x , y ) = E A x + E B y + ∑ 1 ≤ i < x , y < j ≤ n E C ( i , j ) ans(x,y)=EA_{x}+EB_{y}+\sum_{1\leq i <x,y<j\leq n} EC_{(i,j)} ans(x,y)=EAx+EBy+1i<x,y<jnEC(i,j)

对于每一个 x x x,我们要找到一个最优的 y y y,使得 a n s ( x , y ) ans(x,y) ans(x,y) 最小(因为是最小割嘛),然而修改操作只会修改 E A x EA_x EAx ,所以对于每个 x x x,其对应的 y y y 是不会变的。

于是我们可以枚举 i = 1 i=1 i=1 ~ n n n,对于每个 i i i,将他连向的 B B B 集合里面的点造成的贡献加入到线段树里面,显然对于一个点 j j j,他会对 1 1 1 ~ j − 1 j-1 j1 的所有点造成贡献。然后在所有点里面取一个 min ⁡ \min min,加上 A i A_i Ai,就是这个点的贡献了。

然后再对所有点的贡献取一个 min ⁡ \min min 就是最终的答案,可以用线段树维护,修改也可以用线段树轻易解决。

看到这里可能会有人产生疑问:对于一对 ( x , y ) (x,y) (x,y),可能并不存在让 E A x EA_x EAx E B y EB_y EBy 做割边的方案啊,就算有,这个方案里面也不一定要把所有 E C ( i , j )    ( 1 ≤ i < x , y < j ≤ n ) EC_{(i,j)}~~(1\leq i <x,y<j\leq n) EC(i,j)  (1i<x,y<jn) 都给割掉啊?

对于这样的一对不合要求的 ( x , y ) (x,y) (x,y),其实是不会成为最小解的,因为显然它算多了很多,肯定存在另一个 ( x , z ) (x,z) (x,z) ( z , y ) (z,y) (z,y) 比他们小,所以并不会对答案产生影响。

然后还有一个问题,可能最小割根本不需要割 E A EA EA E B EB EB 里面的边,所以我们考虑往 E A EA EA 的末尾放一条长度为 0 0 0 的边,往 E B EB EB 的开头放一条长度为 0 0 0 的边,那么就可以将不割 E A EA EA 里面的边和不割 E B EB EB 里面的边这两种情况考虑进去。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 200010
#define ll long long

int n,m,k;
int a[maxn],b[maxn];
struct nod{int y,z,next;};
nod e[maxn];
int first[maxn];
void buildroad(int x,int y,int z)
{
	static int len=0;
	e[++len]=(nod){y,z,first[x]};
	first[x]=len;
}
ll ans[maxn];
struct node{
	int l,r;
	ll z,lazy;
	node *zuo,*you;
	template<class T>
	node(int x,int y,T *s)
	{
		l=x,r=y;lazy=0;
		if(l<r)
		{
			int mid=l+r>>1;
			zuo=new node(l,mid,s);
			you=new node(mid+1,y,s);
			z=min(zuo->z,you->z);
		}
		else z=s[l],zuo=NULL,you=NULL;
	}
	void pushdown()
	{
		if(lazy!=0)
		{
			z+=lazy;
			if(zuo!=NULL)zuo->lazy+=lazy,you->lazy+=lazy;
			lazy=0;
		}
	}
	void change(int x,int y,int c)
	{
		if(l==x&&r==y)
		{
			lazy+=(ll)c;
			pushdown();
			return;
		}
		pushdown();
		int mid=l+r>>1;
		if(y<=mid)zuo->change(x,y,c),you->pushdown();
		else if(x>=mid)you->change(x,y,c),zuo->pushdown();
		else zuo->change(x,mid,c),you->change(mid+1,y,c);
		z=min(zuo->z,you->z);
	}
};
node *root=NULL;
void work()
{
	root=new node(1,n,b);
	for(int i=1;i<=n;i++)
	{
		for(int j=first[i];j;j=e[j].next)
		root->change(1,e[j].y,e[j].z);
		ans[i]=root->z+a[i];
	}
}

int main()
{
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<n;i++)
	scanf("%d %d",&a[i],&b[i+1]);
	for(int i=1,x,y,z;i<=m;i++)
	scanf("%d %d %d",&x,&y,&z),buildroad(x,y,z);
	work();
	root=new node(1,n,ans);
	printf("%lld\n",root->z);
	for(int i=1,x,y;i<=k;i++)
	{
		scanf("%d %d",&x,&y);
		root->change(x,x,y-a[x]);
		a[x]=y;
		printf("%lld\n",root->z);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值