其中
A
i
A_i
Ai 到
A
i
+
1
A_{i+1}
Ai+1 有容量为
x
i
x_i
xi 的边(
1
≤
i
<
n
1\le i<n
1≤i<n )
B
i
B_i
Bi 到
B
i
+
1
B_{i+1}
Bi+1 有容量为
y
i
y_i
yi 的边(
1
≤
i
<
n
1\le i<n
1≤i<n )
有
m
m
m 个三元组
(
x
,
y
,
z
)
(x,y,z)
(x,y,z) 表示
A
x
A_x
Ax 到
B
y
B_y
By 有容量为
z
z
z 的边
求
A
1
A_1
A1 到
B
n
B_n
Bn 的最大流
之后有
q
q
q 个操作,每个操作给出两个参数
v
v
v 和
w
w
w ,把
x
v
x_v
xv (
A
v
A_v
Av 到
A
v
+
1
A_{v+1}
Av+1 的边的容量)修改成
w
w
w
每次修改之后需要重新求出原图的最大流
2
≤
n
,
m
≤
2
×
1
0
5
2\le n,m\le 2\times 10^5
2≤n,m≤2×105
0
≤
q
≤
2
×
1
0
5
0\le q\le 2\times10^5
0≤q≤2×105
1
≤
x
i
,
y
i
,
z
,
w
≤
1
0
9
1\le x_i,y_i,z,w\le10^9
1≤xi,yi,z,w≤109
Solution
注:下面一律
x
n
=
y
0
=
0
x_n=y_0=0
xn=y0=0 ,
c
a
p
(
u
,
v
)
cap(u,v)
cap(u,v) 为
u
u
u 到
v
v
v 的边的容量
一道比较不错的数据结构题
首先最大流等于最小割
然后我们考虑这个特殊的图,最小割是否有特殊性
很容易得出,最小割一定是
(1)在
A
A
A 部中割掉一条
A
i
A_i
Ai 到
A
i
+
1
A_{i+1}
Ai+1 的边(在
A
A
A 部也可以什么边都不割,这时视为割边
(
A
n
,
A
n
+
1
)
(A_n,A_{n+1})
(An,An+1) )
(2)在
B
B
B 部中割掉一条
B
j
−
1
B_{j-1}
Bj−1 到
B
j
B_j
Bj 的边(同样地可以在
B
B
B 部中不割任何边,但这时视为割边
(
B
0
,
B
1
)
(B_0,B_1)
(B0,B1) )
(3)在上面两个的基础上,把所有满足
u
≤
i
,
v
≥
j
u\le i,v\ge j
u≤i,v≥j 的边
(
A
u
,
B
v
)
(A_u,B_v)
(Au,Bv) 全部割掉
我们看到(3)之后可以得出一个思路,设
f
[
i
]
=
min
j
=
1
n
(
y
j
−
1
+
∑
u
=
1
i
∑
v
=
j
n
c
a
p
(
A
u
,
B
v
)
)
f[i]=\min_{j=1}^n(y_{j-1}+\sum_{u=1}^i\sum_{v=j}^ncap(A_u,B_v))
f[i]=j=1minn(yj−1+u=1∑iv=j∑ncap(Au,Bv))
f
[
i
]
f[i]
f[i] 的意义即是在钦定割边
(
A
i
,
A
i
+
1
)
(A_i,A_{i+1})
(Ai,Ai+1) 的情况下的最小割(不包括边
(
A
i
,
A
i
+
1
)
(A_i,A_{i+1})
(Ai,Ai+1) )
如果我们能够求出
f
[
1
…
n
]
f[1\dots n]
f[1…n] ,那么我们的问题就能解决了
最大流(最小割)就是
min
i
=
1
n
(
f
[
i
]
+
x
i
)
\min_{i=1}^n(f[i]+x_i)
i=1minn(f[i]+xi)
线段树维护
f
[
i
]
+
x
i
f[i]+x_i
f[i]+xi 即可
然后我们考虑如何求
f
[
1
…
n
]
f[1\dots n]
f[1…n]
考虑从
1
1
1 到
n
n
n 按顺序枚举
i
i
i
开一棵线段树
j
j
j 位置的叶子节点储存的是
y
j
−
1
+
∑
u
=
1
i
∑
v
=
j
n
c
a
p
(
A
u
,
B
v
)
y_{j-1}+\sum_{u=1}^i\sum_{v=j}^ncap(A_u,B_v)
yj−1+∑u=1i∑v=jncap(Au,Bv) ,意义是钦定割边
(
A
i
,
A
i
+
1
)
(A_i,A_{i+1})
(Ai,Ai+1) 和
(
B
j
−
1
,
B
j
)
(B_{j-1},B_j)
(Bj−1,Bj) 之后还需要割掉多大的容量,需要割掉的边容量之和再加上
c
a
p
(
B
j
−
1
,
B
j
)
cap(B_{j-1},B_j)
cap(Bj−1,Bj) 即
y
j
y_j
yj
线段树维护区间最小值
每次
i
i
i 从
i
−
1
i-1
i−1 加一过来时,枚举边
(
A
i
,
B
j
)
(A_i,B_j)
(Ai,Bj) ,并在线段树上进行一次区间修改,区间
[
1
,
j
]
[1,j]
[1,j] 加上
c
a
p
(
A
i
,
B
j
)
cap(A_i,B_j)
cap(Ai,Bj) ,这时
f
[
i
]
f[i]
f[i] 为线段树全局最小值
注意一开始叶子
j
j
j 存的值是
y
j
−
1
y_{j-1}
yj−1
复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
Code
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#define p2 p << 1#define p3 p << 1 | 1inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}typedeflonglong ll;constint N =2e5+5, M = N <<2;int n, m, q, a[N], ecnt, nxt[N], adj[N], go[N], val[N];
ll T[M], add[M], b[N], ioi[N];voidadd_edge(int u,int v,int w){
nxt[++ecnt]= adj[u]; adj[u]= ecnt; go[ecnt]= v; val[ecnt]= w;}voidbuild(int l,int r,int p){
add[p]=0;if(l == r)return(void)(T[p]= b[l]);int mid = l + r >>1;build(l, mid, p2);build(mid +1, r, p3);
T[p]=Min(T[p2], T[p3]);}voiddown(int p){
add[p2]+= add[p]; add[p3]+= add[p];
add[p]=0;}voidupt(int p){
T[p]=Min(T[p2]+ add[p2], T[p3]+ add[p3]);}voidchange(int l,int r,int s,int e,int v,int p){if(l == s && r == e)return(void)(add[p]+= v);int mid = l + r >>1;down(p);if(e <= mid)change(l, mid, s, e, v, p2);elseif(s >= mid +1)change(mid +1, r, s, e, v, p3);elsechange(l, mid, s, mid, v, p2),change(mid +1, r, mid +1, e, v, p3);upt(p);}intmain(){int x, y, z;
n =read(); m =read(); q =read();for(int i =1; i < n; i++)
a[i]=read(), b[i +1]=read();while(m--) x =read(), y =read(), z =read(),add_edge(x, y, z);build(1, n,1);for(int i =1; i <= n; i++){for(int e = adj[i], v = go[e]; e; e = nxt[e], v = go[e])change(1, n,1, v, val[e],1);
ioi[i]= T[1]+ add[1];}for(int i =1; i <= n; i++) b[i]= a[i]+ ioi[i];build(1, n,1);
std::cout << T[1]+ add[1]<< std::endl;while(q--){
x =read(); y =read();change(1, n, x, x, y - a[x],1);
a[x]= y;printf("%I64d\n", T[1]+ add[1]);}return0;}