zoukankan      html  css  js  c++  java
  • [CF903G]Yet Another Maxflow Problem

    [CF903G]Yet Another Maxflow Problem

    题目大意:

    (A)类点和(B)类点各(n(nle2 imes10^5))个,所有(A_i)(A_{i+1})有一条权值为(a_i)的有向边,所有(B_i)(B_{i+1})有一条权值为(b_i)的有向边,另有(m(mle2 imes10^5))条从(A_x)(B_y)的权值为有向边。连续(q(qle2 imes10^5))次操作将(A_{v_i})(A_{v_i+1})之间的边的权值改为(w_i)。问每次修改完毕后的从(A_1)(B_n)的最大流。

    思路:

    根据最大流-最小割定理,题目所求相当于每次修改完毕后的最小割。定义(A)类点间的边为(A)类边,(B)类点间的边为(B)类边,(AB)类点间的边为(C)类边。假设两类边各(n-1)条之外还分别有一个边权为(0)的边,那么每次的最小割一定恰好包含一个(A)类边、一个(B)类边和若干(C)类边。由于(B)类边和(C)类边都不会被修改,则对于同一个(A)类边,对应的最优的(B)类边是固定的。方便起见,下文将(B_i)(B_{i+1})的边记作(b_{i+1}),不同于题面描述。

    考虑预处理每个(A)类边对应的最优(B)类边。不难发现,若我们选择了(a_i)(b_j)两条边,要使得(A_1)(B_n)不连通,则我们还需要割去所有连接(A_x,B_y(xle i,yge j))(C)类边,而这也是选择(a_i)(b_j)后的最小割。反过来说,连接(A_x,B_y)(C)类边会对(a_i,b_j(xle i,yge j))的选择产生影响。因此我们可以(1sim n)枚举每个(a_i),用线段树维护对应每个(b_j)所需要的最小割的大小。首先将所有(b_j)加入到线段树中,对于当前枚举到的(a_i),枚举从(A_i)出发的所有(C)类边,若对应的点为(B_j),权值为(w),将区间([1,j])加上(w),表示对于(a_i)(a_i)以后的(A)类边,若还要考虑(b_j)(b_j)以前的(B)类边作为对应边,一定要割去这条(C)类边。而每次插入后线段树最小元素就是对应当前(a_i),由一个(B)类边和若干(C)类边组成的、能与(a_i)构成割的边权和,记这一边权和为(sum),则选择(a_i)时的最小割为(sum+a_i),记作(c_i)

    考虑修改操作,由于(a_i)对应的(B)类边和(C)类边已经确定,每次修改时(a_i)的变化也就是(c_i)的变化。用一些数据结构维护所有(c_i)的最小值即可。时间复杂度(mathcal O((m+q)log n))

    源代码:

    #include<cstdio>
    #include<cctype>
    #include<vector>
    using int64=long long;
    inline int getint() {
    	register char ch;
    	while(!isdigit(ch=getchar()));
    	register int x=ch^'0';
    	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    	return x;
    }
    constexpr int N=2e5+1;
    int64 a[N],b[N],c[N];
    using Edge=std::pair<int,int>;
    std::vector<Edge> e[N];
    class SegmentTree {
    	#define _left <<1
    	#define _right <<1|1
    	private:
    		int64 val[N<<2],tag[N<<2];
    		void push_up(const int &p) {
    			val[p]=std::min(val[p _left],val[p _right]);
    		}
    		void push_down(const int &p) {
    			if(!tag[p]) return;
    			val[p _left]+=tag[p];
    			val[p _right]+=tag[p];
    			tag[p _left]+=tag[p];
    			tag[p _right]+=tag[p];
    			tag[p]=0;
    		}
    	public:
    		void build(const int &p,const int &b,const int &e,const int64 arr[]) {
    			tag[p]=0;
    			if(b==e) {
    				val[p]=arr[b];
    				return;
    			}
    			const int mid=(b+e)>>1;
    			build(p _left,b,mid,arr);
    			build(p _right,mid+1,e,arr);
    			push_up(p);
    		}
    		void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const int64 &v) {
    			if(b==l&&e==r) {
    				val[p]+=v;
    				tag[p]+=v;
    				return;
    			}
    			push_down(p);
    			const int mid=(b+e)>>1;
    			if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),v);
    			if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,v);
    			push_up(p);
    		}
    		int64 query() const {
    			return val[1];
    		}
    	#undef _left
    	#undef _right
    };
    SegmentTree t;
    int main() {
    	const int n=getint(),m=getint(),q=getint();
    	for(register int i=1;i<n;i++) {
    		a[i]=getint(),b[i+1]=getint();
    	}
    	t.build(1,1,n,b);
    	for(register int i=0;i<m;i++) {
    		const int u=getint(),v=getint(),w=getint();
    		e[u].push_back({v,w});
    	}
    	for(register int x=1;x<=n;x++) {
    		for(register auto &j:e[x]) {
    			const int &y=j.first,&w=j.second;
    			t.modify(1,1,n,1,y,w);
    		}
    		c[x]=t.query()+a[x];
    	}
    	t.build(1,1,n,c);
    	printf("%lld
    ",t.query());
    	for(register int i=0;i<q;i++) {
    		const int x=getint(),v=getint();
    		t.modify(1,1,n,x,x,v-a[x]);
    		a[x]=v;
    		printf("%lld
    ",t.query());
    	}
    	return 0;
    }
    
  • 相关阅读:
    从零开始通过webhooks实现前端自动化
    使用rem配置PC端自适应大屏
    Nuxt内导航栏的两种实现方式
    VueX中直接修改数据报错,修改一维数组,二维数组,报错的原因
    在mpvue或者Vue中使用VUEX
    小程序框架MpVue踩坑日记(二)
    小程序mpvue中动态切换echarts图表
    小程序踩坑之不同屏幕下动态改变translate值
    Koa2+MySQL+VUE+ElementIUI搭建简单的后台管理小系统
    小程序框架MpVue踩坑日记(一)
  • 原文地址:https://www.cnblogs.com/skylee03/p/9087266.html
Copyright © 2011-2022 走看看