CF903G Yet Another Maxflow Problem
题意:一张图分为两部分,左右都有 \(n\)个节点,\(a_i \rightarrow a_{i+1}\)连边,\(b_i \rightarrow b_{i+1}\) 连边,容量给出。
有 \(m\) 对\(a_i \rightarrow b_j\)有边,容量给出。
你需要先求出原图从\(a_1\) 到 \(b_n\) 的最大流,然后有 \(q\) 次操作,每次操作给出 \(i\),先修改\(a_i \rightarrow a_{i+1}\) 的边的容量,然后询问从 \(a_1\) 到 \(b_n\) 的最大流。\(n \leq 2\times 10^5\)。
题解:考虑到最大流等于最小割,我们考虑割边的最优策略。
由于所有边都是单向边,所以\(a\)和\(b\)内部的边最多各割掉一条。那么我们就得到了一个做法:枚举删掉的各是哪条边,然后考虑一下有哪些\(a\)到\(b\)的边要割掉,然后取个min。但是这样是\(O(n^2)\)的,还不能支持修改。所以考虑只枚举\(a\),然后算出哪条\(b\)是最优的。设\(f_{i,j}\)表示分别割掉了\(a_i \rightarrow a_{i+1}\)和\(b_j \rightarrow b_{j+1}\)的边的最小割。那么有:
\(f_{i,j}=x_i+y_j+\sum_{a \leq i,b>j}w_{a,b}\)。发现后面的东西是个定值,所以把它算出来就行了。我们把所有\(a\)到\(b\)之间的边排序,那么一条边的贡献相当于区间修改。用线段树来维护就行了。
我们求出对于每个\(i\)最小的\(j\)之后,修改就很简单了。再开一棵线段树维护即可。
时间复杂度:\(O(nlogn)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const ll INF=1e18+9;
struct E{
int u,v;ll w;
E(int _u=0,int _v=0,ll _w=0){u=_u;v=_v;w=_w;}
friend bool operator < (E a,E b){return a.u<b.u;}
}e[202000];
int n,m,q,a[202000],b[202000];
ll tr[808000],laz[808000],S,c[202000];
#define all 1,0,n-1
#define lt k<<1,l,mid
#define rt k<<1|1,mid+1,r
I build(int k,int l,int r){
if(l==r)return tr[k]=(ll)b[l],void();
re mid=(l+r)>>1;build(lt);build(rt);
tr[k]=min(tr[k<<1],tr[k<<1|1]);
}
I add(int k,ll w){tr[k]+=w;laz[k]+=w;}
I push_down(int k){add(k<<1,laz[k]);add(k<<1|1,laz[k]);laz[k]=0;}
I modi(int k,int l,int r,int x,int y,ll w){
if(x>r||y<l)return;
if(x<=l&&r<=y)return add(k,w),void();
if(laz[k])push_down(k);
re mid=(l+r)>>1;
modi(lt,x,y,w);modi(rt,x,y,w);
tr[k]=min(tr[k<<1],tr[k<<1|1]);
}
I buildin(int k,int l,int r){
if(l==r)return tr[k]=c[l],void();
re mid=(l+r)>>1;buildin(lt);buildin(rt);
tr[k]=min(tr[k<<1],tr[k<<1|1]);
}
I revi(int k,int l,int r,int x,ll w){
if(l==r)return tr[k]+=w,void();
re mid=(l+r)>>1;
if(x<=mid)revi(lt,x,w);
else revi(rt,x,w);
tr[k]=min(tr[k<<1],tr[k<<1|1]);
}
int main(){
read(n);read(m);read(q);
F(i,1,n-1)read(a[i]),read(b[i]);
re X,Y,W;
F(i,1,m)read(X),read(Y),read(W),e[i]=E(X,Y-1,W),S+=W;
sort(e+1,e+1+m);
re now=1;build(all);c[0]=INF;
F(i,1,n){
while(now<=m&&e[now].u==i)modi(all,0,e[now].v,e[now].w),now++;
c[i]=(ll)a[i]+tr[1];
// cout<<c[i]<<" ";
}
// cout<<endl;
S=min(S,c[n]);buildin(all);printf("%lld\n",min(S,tr[1]));
while(q--){
read(X);read(W);
revi(all,X,W-a[X]);a[X]=W;
printf("%lld\n",min(S,tr[1]));
}
return 0;
}
/*
4 3 2
1 2
3 4
5 6
2 2 7
1 4 8
4 3 9
1 100
2 100
*/