CF903G Yet Another Maxflow Problem
题意
有 (A,B) 两列点, 每列都有 (n) 个点 ((n le 2 imes 10^5)).
对于任意 (i in [1,n-1]), (a_i) 与 (a_{i+1}), (b_i) 与 (b_{i+1}) 之间都有一条给定权值的连边,
(A) 和 (B) 之间有 (m) 条给定权值的边 ((m le 2 imes 10^5)).
有 (q) 个操作 (( q le 2 imes 10^5)),
每次操作要求修改一条 (a_i) 与 (a_{i+1}) 之间的边, 并询问修改后 (a_1) 到 (b_n) 的最大流.
思路
首先, 最大流等于最小割.
假设我们选定了 (i,j), 要割掉 ((a_i,a_{i+1})) 和 ((b_j,b_{j+1})) 这两条边,
那么 (A,B) 之间要割的边就是左端点小于等于 (i), 右端点大于等于 (j+1) 的边,
即 (E={(x,y)| x le i, y ge j+1}).
修改操作只会修改 (A) 之间的边, 所以我们可以预处理出 选定了 (A) 中的割边后所需要的最小代价.
我们可以从小到大枚举 (i), 表示选择要割掉 ((a_i,a_{i+1})).
设当前枚举到 (k), 这时, 我们在 (B) 中选取某些点的代价就要加上 (A,B) 之间左端点小于等于 (k) 的边的代价.
对于一条边 ((x,y)), 其中 (x le k),
若要割掉 (B) 中的 ((b_j,b_{j+1})) 这条边, 且 (j le y), 则就要加上 (w(x,y)) 的代价.
所以, 我们可以用线段树来维护当前选取 (B) 中的点的最小代价.
处理好了 选定了 (A) 中的 (每条) 割边后所需要的最小代价 后, 我们可以把这些最小代价加上 (A) 中对应边的权值, 再把它们扔进线段树里,
这时, 线段树的最小值就代表从 (a_1) 到 (b_n) 的最小割.
然后每次修改 (A) 中边的权值时, 在线段树上单点修改就行了, 询问就区间查询最小值.
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int _=2e5+7;
const int __=8e5+7;
const ll inf=1e17;
int n,m,q;
ll wgt[2][_],cst[_],minx[__],tag[__]; // 0: A 1: B
struct edge{
int x,y;
ll w;
}e[_];
void build(int k,int l,int r,bool id){
tag[k]=0;
if(l==r){
if(!id){
minx[k]=wgt[0][l]+cst[l];
}
else minx[k]=wgt[1][l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid,id);
build(k<<1|1,mid+1,r,id);
minx[k]=min(minx[k<<1],minx[k<<1|1]);
}
bool rule_x(edge a,edge b){ return a.x<b.x; }
void init(){
cin>>n>>m>>q; n--;
for(int i=1;i<=n;i++)
scanf("%lld%lld",&wgt[0][i],&wgt[1][i]);
for(int i=1;i<=m;i++)
scanf("%d%d%lld",&e[i].x,&e[i].y,&e[i].w);
sort(e+1,e+1+m,rule_x);
build(1,0,n,1);
}
void upd(int k,ll w){
minx[k]+=w;
tag[k]+=w;
}
void psd(int k){
if(!tag[k]) return;
upd(k<<1,tag[k]);
upd(k<<1|1,tag[k]);
tag[k]=0;
}
void modify(int k,int l,int r,int x,int y,ll w){
if(l>=x&&r<=y){ upd(k,w); return; }
psd(k);
int mid=(l+r)>>1;
if(x<=mid) modify(k<<1,l,mid,x,y,w);
if(y>mid) modify(k<<1|1,mid+1,r,x,y,w);
minx[k]=min(minx[k<<1],minx[k<<1|1]);
}
ll query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y) return minx[k];
psd(k);
int mid=(l+r)>>1;
ll t1=inf,t2=inf;
if(x<=mid) t1=query(k<<1,l,mid,x,y);
if(y>mid) t2=query(k<<1|1,mid+1,r,x,y);
return min(t1,t2);
}
void pre(){
int p=1;
for(int i=1;i<=n+1;i++){
while(p<=m&&e[p].x<=i){
modify(1,0,n,0,e[p].y-1,e[p].w);
p++;
}
if(i==n+1) cst[0]=query(1,0,n,0,n);
else cst[i]=query(1,0,n,0,n);
}
build(1,0,n,0);
}
void run(){
printf("%lld
",query(1,0,n,0,n));
int x; ll w;
for(int i=1;i<=q;i++){
scanf("%d%lld",&x,&w);
modify(1,0,n,x,x,-wgt[0][x]+w);
wgt[0][x]=w;
printf("%lld
",query(1,0,n,0,n));
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
#endif
init();
pre();
run();
return 0;
}