题目大意: 有一张二分图,左边有 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) (1≤i<x,y<j≤n) 都给割掉。
设
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+1≤i<x,y<j≤n∑EC(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 j−1 的所有点造成贡献。然后在所有点里面取一个 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) (1≤i<x,y<j≤n) 都给割掉啊?
对于这样的一对不合要求的 ( 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);
}
}
本文探讨了一种解决二分图中最大流问题的算法,通过转化为最小割问题进行求解。介绍了如何利用线段树维护边的流量,并在修改操作下快速更新最大流,适用于动态变化的图结构。

300

被折叠的 条评论
为什么被折叠?



