[CF903G]Yet Another Maxflow Problem

[CF903G]Yet Another Maxflow Problem

题目大意:

\(A\)类点和\(B\)类点各\(n(n\le2\times10^5)\)个,所有\(A_i\)\(A_{i+1}\)有一条权值为\(a_i\)的有向边,所有\(B_i\)\(B_{i+1}\)有一条权值为\(b_i\)的有向边,另有\(m(m\le2\times10^5)\)条从\(A_x\)\(B_y\)的权值为有向边。连续\(q(q\le2\times10^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(x\le i,y\ge j)\)\(C\)类边,而这也是选择\(a_i\)\(b_j\)后的最小割。反过来说,连接\(A_x,B_y\)\(C\)类边会对\(a_i,b_j(x\le i,y\ge j)\)的选择产生影响。因此我们可以\(1\sim 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\n",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\n",t.query());
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/skylee03/p/9087266.html