CF903G Yet Another Maxflow Problem 解题报告 (线段树)
CF903G Yet Another Maxflow Problem
题意
有 \(A,B\) 两列点, 每列都有 \(n\) 个点 \((n \le 2 \times 10^5)\).
对于任意 \(i \in [1,n-1]\), \(a_i\) 与 \(a_{i+1}\), \(b_i\) 与 \(b_{i+1}\) 之间都有一条给定权值的连边,
\(A\) 和 \(B\) 之间有 \(m\) 条给定权值的边 \((m \le 2 \times 10^5)\).
有 \(q\) 个操作 \(( q \le 2 \times 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\n",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\n",query(1,0,n,0,n));
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
#endif
init();
pre();
run();
return 0;
}