Educational Codeforces Round 34 G. Yet Another Maxflow Problem 线段树实现最小割

100 篇文章 0 订阅
86 篇文章 0 订阅

题目链接: https://codeforces.com/contest/903/problem/G

题意:

在理解网络流的前提下简化的题目大概是,源点向 A 1 A_{1} A1 连流量 i n f inf inf , B n B_n Bn 向汇点连流量 i n f inf inf,对于每个 i ∈ [ 1 , n − 1 ] i\in[1,n-1] i[1,n1] 都有一条 A i → A i + 1 A_i\rightarrow A_{i+1} AiAi+1 以及 B i → B i + 1 B_i\rightarrow B_{i+1} BiBi+1 的边,流量均已知,同时告诉你 m m m A x → B y A_x\rightarrow B_y AxBy 的流量已知的边。 现在要进行 q q q 次操作,每次操作改变一条 A x → A x + 1 A_x\rightarrow A_{x+1} AxAx+1 的边的流量,每次改变后问你当前的最大流。

做法:

表示 2700 2700 2700 的题目不愧是 2700 2700 2700的题目,别说自己想了…连看人家的题解都看了半天才明白… (在自闭的边缘徘徊)

自己表述可能不太好,就借着大佬的博客内容稍微说一说。

最小割为什么被称之为最小割,因为:

A A A 侧的点中,若割掉了 ( A x → A x + 1 ) (A_x→A_{x+1}) (AxAx+1),那么所有的边 ( A y → A y + 1 ) ( y > x ) (A_y→A_{y+1})(y>x) (AyAy+1)(y>x) 失去意义。
B B B 侧的点中,若割掉了 ( B x → B x + 1 ) (B_x→B_{x+1}) (BxBx+1),那么所有的边 ( B y → B y + 1 ) ( y < x ) (B_y→B_{y+1})(y<x) (ByBy+1)(y<x) 失去意义

我们知道的是,在 A A A 侧和 B B B 侧中一定有一遍是满足上述条件的,只要一侧满足,那么另一侧一定能找到一个刚好符合的点。

我们假设 ( A x → A x + 1 ) (A_x→A_{x+1}) (AxAx+1) ( B x → B x + 1 ) (B_x→B_{x+1}) (BxBx+1) 是符合我们要的两条边,那么 ( A u → B v ) ( u ≤ x , y < v ) (A_u→B_v)(u≤x,y<v) (AuBv)(ux,y<v) 都是需要被割掉的边集。

我们从 A A A 侧的点 1 1 1 开始往 n n n 枚举,不断令该点之前的流量变为 i n f inf inf 来尝试,如果是从这个往后因为满流而断掉所需要的最大流量。

线段树里存放是的点 B B B 的合法流量,点 j j j 一点代表的是 B [ j − 1 ] + ∑ u = 1 i ∑ v = j n c a p ( A u → B v ) B[j-1]+\sum_{u=1}^{i}\sum_{v=j}^{n}cap(A_u\rightarrow B_v) B[j1]+u=1iv=jncap(AuBv) ,表示我们在确定了 A A A 侧的 i i i B B B 侧的 j j j 之后需要割的流量。 我们每次都取 B B B 侧的最小值即可。

我一开始的时候对这个做法很是疑惑,但是模了之后也慢慢理解了。。觉得很是神奇的一道题目。

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int maxn=200005;
ll mx[maxn<<2],laz[maxn<<2];
ll n,m,q,A[maxn],B[maxn],flow[maxn];
vector<pll> ve[maxn];
void build(int l,int r,int rt){
    if(l==r) {
        mx[rt]=(ll)B[l-1];
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    mx[rt]=min(mx[lson],mx[rson]);
}
void deal(int rt,ll v){
    mx[rt]+=v,laz[rt]+=v;
}
void push_down(int rt){
    if(laz[rt]){
        deal(lson,laz[rt]);
        deal(rson,laz[rt]);
        laz[rt]=0ll;
    }
}
void update(int l,int r,int rt,int ql,int qr,ll v){
    if(ql<=l&&r<=qr){
        deal(rt,v);
        return ;
    }
    push_down(rt);
    if(ql<=mid) update(l,mid,lson,ql,qr,v);
    if(qr>mid) update(mid+1,r,rson,ql,qr,v);
    mx[rt]=min(mx[lson],mx[rson]);
}
multiset<ll> S;
int main(){
    scanf("%lld%lld%lld",&n,&m,&q);
    rep(i,1,n-1) scanf("%lld%lld",&A[i],&B[i]);
    rep(i,1,m){
        ll x,y,c; scanf("%lld%lld%lld",&x,&y,&c);
        ve[x].push_back({y,c});
    }
    build(1,n,1);
    //printf("%d\n",ve[1].size());
    rep(i,1,n){
        for(auto p:ve[i]){
            update(1,n,1,1,p.first,p.second);
        }
        flow[i]=mx[1];
        S.insert(A[i]+flow[i]);
    }
    printf("%lld\n",*(S.begin()));
    rep(k,1,q){
        int x,y;scanf("%d%d",&x,&y);
        S.erase(S.find(A[x]+flow[x]));
        A[x]=y; S.insert(A[x]+flow[x]);
        printf("%lld\n",*(S.begin()));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值