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
*/
posted @ 2020-07-13 21:39  Purple_wzy  阅读(101)  评论(0编辑  收藏  举报