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;
}
posted @ 2020-01-14 16:52  BruceW  阅读(102)  评论(0编辑  收藏  举报