題解 CF903G Yet Another Maxflow Problem
阿新 • • 發佈:2021-11-03
Solution CF903G Yet Another Maxflow Problem
題意描述
給定一張圖,左右各\(n\)個節點,\(A_i\to A_{i+1}\)連邊,\(B_i\to B_{i+1}\)連邊,容量給出。
有\(m\)對\(A_i\to B_j\)有邊,容量給出。
你需要先求出原圖從\(A_1\)到\(B_n\)的最大流,然後有\(q\)次操作,每次操作給出\(i\),先修改\(A_i\to A_{i+1}\)的邊的容量,然後詢問從\(A_1\)到\(B_n\)的最大流。
Solution:
顯然最大流先轉最小割,則考慮割掉哪些邊最優。
可以發現,割邊有四種選擇:左部鏈上割or不割,右部鏈上割or不割,乘法原理得到四種選擇。
我們不希望過多的分類討論,因此可以在\(A_1\)上面增加一條邊表示左部不割邊,在\(B_n\)下面增加一條邊表示右部不割邊。那麼問題就是在左部割若干條邊,右部割若干條邊,再割掉中間與\(A_1,B_n\)連通的邊,求所有方案最小代價。
在左部或右部同時割掉多條邊顯然是不優的,因為只有斷掉的與\(A_1,B_n\)最近的邊產生了割的效果,於是問題可以抽象為一個式子:
\[Min\{A_x+B_y+\sum\limits_{u,v\in E,u<x,v\geq y}W(u,v)\} \]此時回到題目:\(q\)次操作,每次操作給出\(i\),先修改\(A_i\to A_{i+1}\)的邊的容量,然後詢問從\(A_1\)
注意到修改僅在左部進行,因此對於割掉一條左部邊\(A_x\to A_{x+1}\),所對應的
\[B_y+\sum\limits_{u,v\in E,u<x,v\geq y}W(u,v) \]是不變的。考慮使用線段樹,將\(B\)放到線段樹上,從\(1\)到\(n\)加入\(A_x\),每到一個新點就列舉該點的邊集\(E(x,v)\),線上段樹上對應區間\(v\sim n\)進行區間加,然後求全域性最小值。求完後將
\[Min\{A_x+B_y+\sum\limits_{u,v\in E,u<x,v\geq y}W(u,v)\} \]放入一棵新的線段樹,之後的操作就是單點修改\(+\)
程式碼:
#include<bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
#define N 200005
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
#define il inline
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
il int read(){
int w=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return w*h;
}
struct Edge{
int next,to,val;
}edge[N];
int n,m,q;
int head[N],num;
int A[N],B[N],res[N];
void add(int u,int v,int w){
edge[++num].next=head[u];
edge[num].to=v;
edge[num].val=w;
head[u]=num;
}
namespace SGT{
int Min[N<<2],Tag[N<<2];
void pushup(int k){Min[k]=min(Min[ls],Min[rs]);}
void pushdown(int k){
if(!Tag[k])return;
Min[ls]+=Tag[k];Min[rs]+=Tag[k];
Tag[ls]+=Tag[k];Tag[rs]+=Tag[k];
Tag[k]=0;
}
void build(int k,int l,int r){
Tag[k]=0;
if(l==r){
Min[k]=B[l];
return;
}
build(ls,l,mid);
build(rs,mid+1,r);
pushup(k);
}
void modify(int k,int l,int r,int x,int y,int val){
if(l>=x&&r<=y){
Min[k]+=val;
Tag[k]+=val;
return;
}
pushdown(k);
if(x<=mid)modify(ls,l,mid,x,y,val);
if(mid<y)modify(rs,mid+1,r,x,y,val);
pushup(k);
}
}
signed main(){
n=read();m=read();q=read();
for(int i=2;i<=n;i++)A[i-1]=read(),B[i]=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
add(u,v,w);
}
SGT::build(1,1,n);
for(int i=1;i<=n;i++){
for(int j=head[i];j;j=edge[j].next){
int v=edge[j].to,w=edge[j].val;
SGT::modify(1,1,n,1,v,w);
}
res[i]=SGT::Min[1];
}
for(int i=1;i<=n;i++)B[i]=res[i]+A[i];
SGT::build(1,1,n);
printf("%lld\n",SGT::Min[1]);
while(q--){
int x=read(),val=read();
SGT::modify(1,1,n,x,x,val-A[x]);
A[x]=val;
printf("%lld\n",SGT::Min[1]);
}
return 0;
}